Flash Weather - Universität Osnabrück

Werbung
Flash Weather
Ein Generator für Macromedia Flash
zur interaktiven Visualisierung XML basierter Daten
Diplomarbeit
von
Ralf Kunze
betreut von
Prof. Dr. Oliver Vornberger
Prof. Dr. Horst Malchow
Fachbereich Mathematik/Informatik
Universität Osnabrück
15.06.2001
I
Vorwort
Diese Diplomarbeit entstand an der Universität Osnabrück im Fachbereich Informatik.
Sie ist der schriftliche Teil der Diplomprüfungen für meinen Abschluß als Diplom Systemwissenschaftler.
Danksagung
An dieser Stelle möchte ich folgenden Personen für ihre Unterstützung bei der Erstellung
dieser Diplomarbeit danken:
• Herrn Prof. Dr. Oliver Vornberger für die sehr gute Betreuung der Arbeit, seine
Hinweise und Anregungen und der Beschaffung der Wetterdaten
• Herrn Prof. Dr. Horst Malchow für die Betreuung der Arbeit seitens der Systemwissenschaft
• Olaf Müller, Benjamin Stark, Stefan Rauch und Tanja Schniederberend für konstruktive Vorschläge und das Korrekturlesen dieser Arbeit
• Welf Spörlein, Ina Niehaus und Andreas Thyen für das Korrekturlesen dieser Arbeit
• Michael Dirska für die Bereitstellung eines Computers und der benötigten Software im Fachbereich Informatik
• Meiner Mutter, die mir das Studium der Angewandten Systemwissenschaft erst
ermöglicht und mich die ganze Zeit über unterstützt hat
Warenzeichen
In dieser Arbeit werden eingetragene Warenzeichen, Handelsnamen und Gebrauchsnamen verwendet. Auch wenn diese nicht als solche gekennzeichnet sind, gelten die entsprechenden Schutzbestimmungen. Die Verwendung des Macromedia Flash File Format
(SWF) Software Development Kit (SDK) unterliegt den Bestimmungen des Macromedia
Flash File Format (SWF) SDK License Agreement [Macr2001]. Apache Xerces unterliegt den Bestimmungen der Apache Software License [Apac2001].
II
Inhaltsverzeichnis
1
Einleitung
1
2
Macromedia Flash
5
2.1
Vor- und Nachteile von Macromedia Flash . . . . . . . . . . . . . . . . .
5
2.2
Der Flash Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
2.3
Das Shockwave Flash (SWF) Dateiformat . . . . . . . . . . . . . . . . .
9
2.4
3
5
Datentypen in SWF . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.2
Darstellung eines Flashfilmes . . . . . . . . . . . . . . . . . . . 15
SWF – SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
eXtensible Markup Language
21
3.1
Der Standard XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2
Einsatzgebiete und Anwendung von XML . . . . . . . . . . . . . . . . . 25
3.3
Die Document Type Definition – DTD . . . . . . . . . . . . . . . . . . . 26
3.4
4
2.3.1
3.3.1
Dokumentanalyse . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.3.2
Die DTD-Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . 27
XML Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.4.1
XML-Parser Aplication Programming Interface . . . . . . . . . . 31
3.4.2
Welche Parser gibt es? . . . . . . . . . . . . . . . . . . . . . . . 37
Das Java Native Interface
39
4.1
Programmentwicklung mit JNI . . . . . . . . . . . . . . . . . . . . . . . 40
4.2
Datentypen in JNI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Clipping
47
5.1
Grundlagen des Clipping . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.2
Clipping von Punkten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.3
Clipping von Linien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.4
Clipping von Polygonen . . . . . . . . . . . . . . . . . . . . . . . . . . 49
III
6
Realisierung
6.1
6.2
6.3
6.4
7
52
Erstellen einer Java API für das C++ SWF-SDK . . . . . . . . . . . . . . 52
6.1.1
Verwaltung von C++ Objekten auf der Javaseite
. . . . . . . . . 52
6.1.2
Konkrete Umsetzung: Entwicklung der SWF API . . . . . . . . . 55
6.1.3
Beseitigung von Fehlern in der Klasse HFPolygon . . . . . . . 58
Das Datenformat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
6.2.1
Entwicklung der SKF-DTD . . . . . . . . . . . . . . . . . . . . 61
6.2.2
Konvertierung in das Schnittstellenformat . . . . . . . . . . . . . 67
Der Flash Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.3.1
Dynamik und Flexibilität des Generators . . . . . . . . . . . . . 69
6.3.2
Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.3.3
Einbinden von Fonts . . . . . . . . . . . . . . . . . . . . . . . . 75
Clipping von SKF-Dateien . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.4.1
Clipping von primitiven Objekten . . . . . . . . . . . . . . . . . 78
6.4.2
Clipping von Polygonen mit Bezierkurven . . . . . . . . . . . . . 79
6.4.3
Clipping von Kontrolltags . . . . . . . . . . . . . . . . . . . . . 83
6.4.4
Der Clipping-Datenfluß . . . . . . . . . . . . . . . . . . . . . . 84
6.4.5
Die Clipper-Objekte . . . . . . . . . . . . . . . . . . . . . . . . 85
Anwendung
88
7.1
Benötigte Hard- und Software . . . . . . . . . . . . . . . . . . . . . . . 88
7.2
Anwendung der Java SWF API . . . . . . . . . . . . . . . . . . . . . . . 89
7.3
Erstellen eines Flashfilmes aus einer SKF-Datei . . . . . . . . . . . . . . 91
7.4
Clippen einer SKF-Datei . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.5
Beispielfilme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
8
Mögliche Einsatzgebiete
98
9
Resumée
100
10 Ausblick
102
IV
A Das SKF Dateiformat
105
B Die Java SWF API
113
C Flashweather-Bibliotheken
114
D Die calcQuadIntersec Methode der Klasse Polygon
116
E Inhalt der CD-Rom
120
F Literaturverzeichnis
121
Erklärung
V
Abbildungsverzeichnis
2.1
Pixel- und Vektorgrafik im Vergleich
. . . . . . . . . . . . . . . . . . . . .
7
2.2
Vergrößerung einer Pixel- und einer Vektorgrafik
. . . . . . . . . . . . . . .
8
2.3
Dateistruktur einer SWF-Datei
2.4
Snapshot eines Flashfilmes . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.1
Verarbeitungsprozeß für SGML . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2
Zusammenhang zwischen SGML, XML, HTML, CSS und DSSSL . . . . . . . 24
3.3
Grafische Darstellung des zum Quellcode 3.3 gehörenden DOM-Trees . . . . . 33
3.4
SAX-Parser-Events beim Parsen eines XML-Dokumentes . . . . . . . . . . . 35
4.1
Javaklassen können mit der Java Virtual Machine auf verschiedenen Betriebssystemen ausgeführt werden . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.2
JNI als Schnittstelle zwischen Java und C++ . . . . . . . . . . . . . . . . . . 40
4.3
Schritte der Programmentwicklung mit JNI . . . . . . . . . . . . . . . . . . 41
5.1
Mögliche Lagen einer Linie im Bezug zum Clippingrechteck [Fell1992] . . . . 48
5.2
Bereichscodes des Cohen and Sutherland Algorithmus [Fell1992] . . . . . . . 49
5.3
Polygonclipping
5.4
Polygonclipping nach Sutherland and Hodgman . . . . . . . . . . . . . . . . 50
6.1
Mögliche Zeiger eines C-Objektes bei Mehrfachvererbung . . . . . . . . . . . 53
6.2
Datenfluß bei der Generierung eines Flashfilmes . . . . . . . . . . . . . . . . 60
6.3
Ausmaße eines TrueTypeFont-Buchstaben . . . . . . . . . . . . . . . . . . . 75
6.4
Horizontaler Abstand zwischen zwei Buchstaben . . . . . . . . . . . . . . . . 77
6.5
Eine quadratische Bezierkurve . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.6
Verschiedene Arten von Schnittpunkten mit einer Bezierkurve . . . . . . . . . 81
6.7
Alte und neue Punkte der Bezierkurve . . . . . . . . . . . . . . . . . . . . . 82
6.8
Verteilung der Daten über den SAXMultiplexer . . . . . . . . . . . . . . 84
7.1
Ausschnitt aus einem mit der Java SWF API erstellten Flashfilm . . . . . . . . 89
7.2
Einzelne Schritte des Clippings . . . . . . . . . . . . . . . . . . . . . . . . 94
7.3
Momentaufnahme der vorhergesagten Temperaturwerte für Afrika . . . . . . . 95
7.4
Mit dem Clipper erzeugten Ausschnitt aus der Karte 7.3 . . . . . . . . . . . . 96
7.5
Prognostizierte Regenmengen für Europa . . . . . . . . . . . . . . . . . . . 97
. . . . . . . . . . . . . . . . . . . . . . . . 13
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
VI
Tabellenverzeichnis
2.1
Verfügbarkeit des Macromedia Flash Players (Stand: März 2001)
. . . . . . .
6
2.2
Die wichtigsten Datentypen im SWF-Fileformat . . . . . . . . . . . . . . . . 12
2.3
Der RECT Datentyp im SWF-Fileformat
2.4
Dateiheader eines Flashfilmes
2.5
Recordheader für Tags, die eine Gesamtlänge von 63 Byte nicht überschreiten
2.6
Recordheader für Tags, die eine Gesamtlänge von 63 Byte überschreiten . . . . 14
2.7
Struktur des DoAction-Tags
2.8
Struktur des ActionGetURL-Tags . . . . . . . . . . . . . . . . . . . . . . . 15
2.9
Lowlevelklassen des SWF-SDKs . . . . . . . . . . . . . . . . . . . . . . . 18
. . . . . . . . . . . . . . . . . . . 13
. . . . . . . . . . . . . . . . . . . . . . . . 13
. 14
. . . . . . . . . . . . . . . . . . . . . . . . . 14
2.10 Highlevelklassen des SWF-SDKs . . . . . . . . . . . . . . . . . . . . . . . 18
3.1
Attributtypen in XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1
Primitive Datentypen in JNI . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.2
Komplexe Datentypen bei der Nutzung von JNI . . . . . . . . . . . . . . . . 46
4.3
Methoden zum Umgang mit String-Objekten . . . . . . . . . . . . . . . . . . 46
VII
Quellcodeverzeichnis
2.1
Durch Parsen sichtbar gemachte Tags des SWF Filmes (Snapshot in Abb. 7.5) . . 16
2.2
Implementation zur Erstellung eines Flashfilmes (siehe auch Abb. 7.5, Seite 97)
3.1
person.dtd: Einfache DTD zur Strukturierung von Personendaten . . . . . . . . 29
3.2
Eine mögliche Ausprägung der person.dtd . . . . . . . . . . . . . . . . . . . 30
3.3
XML Ausprägung
3.4
ExampleDomParser.java: Verwendung eines Xerces-J DOMParsers . . . . . . . 34
3.5
MySAXHandler.java: Handler zur Ausgabe der Ereignisse eines SAX-Parsers . . 36
4.1
HelloWorld.java: Klasse mit einer native-Methode . . . . . . . . . . . . . . . 42
4.2
HelloWorld.h: Das mit javah -jni erzeugte Headerfile . . . . . . . . . . . 43
4.3
HelloWorld.c: Die Implementation auf der C++ Seite . . . . . . . . . . . . . . 43
6.1
Methode zur Umwandlung eines Pointers in ein Bytearray . . . . . . . . . . . 53
6.2
Methode zur Umwandlung eines Bytearrays in den entsprechenden Pointer . . . 54
6.3
HFObject Klasse, welche HFObject.cc für Java zugänglich macht
6.4
Die zu HFObject.java gehörige native-Klasse f3sdk highlevel HFObject.cc . . . 57
6.5
Methoden zur Umwandlung absoluter in relative Koordinaten
6.6
Dynamische Instanziierung eines Objektes . . . . . . . . . . . . . . . . . . . 70
6.7
Methode parse aus der Klasse Ppolygon zur Verarbeitung des polygon-Tags 74
6.8
Polygon.java: Die Intersec Methode für gerade Linienstücke . . . . . . . . . . 87
7.1
Implementation zur Erstellung eines Flashfilms . . . . . . . . . . . . . . . . 90
7.2
Nutzung des SKF-Generators . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.3
Nutzung des SKF-Clippers . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.4
Zu clippende SKF-Datei
19
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
. . . . . . . 56
. . . . . . . . . 59
. . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1 Einleitung
1
1 Einleitung
Das Internet bietet eine gewaltige Informationsvielfalt. Fast zu jedem Thema und jeder Frage gibt es eine Internetseite mit den entsprechenden Informationen. Wie aber
kann man sich in diesem Informationsdschungel noch zurechtfinden? Es kamen erste
Suchmaschinen auf, welche versuchen, die existierenden Internetseiten zu erfassen, um
sie nach Stichworten durchsuchbar zu machen. Einige Internetdienste sind z.B. Alltheweb1 , Yahoo2 oder Fireball3 . Die Informationssuche ist dadurch vereinfacht worden, daß
man in der Lage ist, durch eine geschickte Aneinanderreihung und Verknüpfung von
Stichwörtern das Suchergebnis soweit einzuschränken, daß lediglich eintausend oder
vielleicht sogar nur einhundert Resultate geliefert werden und nicht einige zehntausend.
Die Informationssuche über die Suchdienste im Internet ist aber nur der erste Schritt.
Wenn eine Webseite gefunden wurde, die die gewünschte Information enthält, müssen
diese noch lange nicht gut aufbereitet sein. Es kann passieren, daß die Informationen
unzulänglich visualisiert werden, wenn sie überhaupt für den Besucher einer Webseite
grafisch aufbereitet sind. Vor allem bei statistischen Daten ist die grafische Darstellung
unerläßlich, denn das Sprichwort Ein Bild sagt mehr als tausend Worte “ ist durchaus
”
berechtigt. Zahlenkolonnen angeordnet in Spalten oder Tabellen erschweren es dem Betrachter die gewünschte Information aus den Daten zu erhalten. Ein gutes Beispiel hierfür
sind Wetterdaten.
Werden die Rohdaten einer Wetterprognose betrachtet, wird man sehr wahrscheinlich
nicht die gewünschte Information erhalten, z.B. welche Temperatur an einem bestimmten
Tag an einem bestimmten Ort herrschen wird. Hierzu müßte erst die genaue geographische Lage des Ortes bekannt sein, um in den Rohwetterdaten nach der Temperatur an den
entsprechenden Koordinaten (welche gegebenenfalls noch transformiert werden müssen)
zu suchen. Nicht ohne Grund sind Wettervorhersagen im Fernsehen oder in Zeitungen in
Bildern oder Animationen grafisch aufbereitet.
Auch die Wetterinformationsdienste im Internet (z.B. Wetteronline4 oder Donnerwetter5 )
präsentieren ihre Prognosen auf grafische Art und Weise. Sie werden meist als Einzelbilder oder animierte GIF6 -Grafiken dargestellt. Diese pixelorientierten Grafiken sind
nicht verlustfrei zu vergrößern und erlauben auch keine Interaktion mit dem Betrachter. Hier sollten neue Wege beschritten werden, um hochwertige Grafiken zu erzeugen,
welche verlustfrei skalierbar sind und vom Betrachter manipuliert werden können, damit
nur die gewünschten Daten zu sehen sind oder ein bestimmter Ort auf der Wetterkarte
näher betrachtet werden kann. So kann jeder Betrachter individuell die Karten nach seinen Bedürfnissen gestalten.
Besonders geeignet erscheint hierfür das Flashformat von Macromedia. Es ist ein vektorbasiertes Format, welches auch Interaktionen mit dem Benutzer ermöglicht. Der Nachteil
dieses Formates ist jedoch, daß es noch keine Software gibt, um Flashfilme aus vorgegebenen Daten dynamisch zu erstellen.
1
www.alltheweb.com
www.yahoo.com
3
www.fireball.de
4
www.wetteronline.de
5
www.donnerwetter.de
6
Graphics Interchange Format von Compuserve
2
1 Einleitung
2
Exemplarisch werden in dieser Diplomarbeit von Benjamin Stark [Stark2001] vektorisierte Wetterdaten visualisiert.
Problemstellung
Seit Macromedia 1998 das Dateiformat von Shockwave Flash (SWF) offen gelegt und
gleichzeitig ein Software Development Kit bereitgestellt hat, können Flashfilme über eine Programmierschnittstelle erzeugt werden, ohne daß die eigentliche grafische Entwicklungsumgebung Flash Studio zum Erstellen solcher Grafiken verwendet werden muß.
Damit dies aber nicht zur Folge hat, daß jeder Flashfilm von Hand programmiert werden
muß, sollen sie automatisch über einen Generator erstellt werden, der ein XML basiertes
Format einliest, in dem die Daten für die Flashfilme enthalten sind.
Die Aufgabenstellung dieser Diplomarbeit ist die Erstellung eines Eingabeformates und
eines Generators, der aus den Eingabedaten einen Flashfilm generieren kann. Hinzu
kommt die Entwicklung eines Java Application Programming Interface (API) für das
Software Development Kit und ein Werkzeug, welches es erlaubt, Flashfilme in einzelne
Kacheln zu zerlegen, damit ein sehr großer Film in einzelne Teile aufgeteilt werden kann.
Die vorliegende Diplomarbeit beschäftigt sich exemplarisch mit der Darstellung von
Wetterprognosedaten, daher auch der Titel Flash Weather “. Die Erkenntnisse sollen
”
jedoch generell auf die Visualisierung beliebiger raum- und zeitbezogener Daten übertragbar sein.
Aufbau der Arbeit
Diese Diplomarbeit ist in drei wesentliche Teile gegliedert. Der erste Abschnitt behandelt
die Grundlagen, im zweiten wird die Realisierung der Aufgabenstellung beschrieben und
im letzten Abschnitt folgen Zusammenfassung und Ausblick.
Im ersten Teil wird in Kapitel 2 Macromedia Flash vorgestellt. Die Vor- und Nachteile werden erläutert und ein kurzer Abriß über die Entstehungsgeschichte des Formates
gegeben. Zusätzlich wird auf das Flash Dateiformat und das zugehörige Software Development Kit von Macromedia eingegangen, um den Aufbau von Flashfilmen und ihre
Erzeugung darzustellen.
Kapitel 3 befaßt sich mit den Einsatzgebieten und der Arbeitsweise der Extensible Markup Language (XML). Da XML die zweite Grundlage dieser Arbeit ist, wird das Thema
ausführlich behandelt, insbesondere die Erstellung einer XML-Anwendung.
In Kapitel 4 wird das Java Native Interface (JNI) vorgestellt, da es in dieser Arbeit
notwendig ist, das in C++ programmierte Software Development Kit von Macromedia
für die Programmiersprache Java zugänglich zu machen. Es werden die grundlegenden
Schritte erläutert, wie eine JNI Application erstellt wird.
Das Clipping in Kapitel 5 schließt den zweiten Teil der Arbeit ab. In diesem Kapitel
wird darauf eingegangen, wie ein beliebiger rechteckiger Ausschnitt aus den Gesamtdaten gewählt werden kann, um daraus einen Flashfilm zu erstellen, und welche algorithmischen und mathematischen Verfahren dafür notwendig sind.
1 Einleitung
3
Im zweiten Abschnitt der Arbeit wird auf die Realisierung der Aufgabenstellung eingegangen. Kapitel 6.1 erläutert die Erstellung einer API für das Macromedia Software
Development Kit und die dabei auftretenden Probleme.
Kapitel 6.2 widmet sich ausführlich dem Datenformat, welches als Basis für den Generator dienen. Die Implementation des Generators, der Flashfilme generieren kann, wird
in Kapitel 6.3 beschrieben.
Im letzten Teil werden die Ergebnisse und Erfahrungen zusammengefaßt und ein Ausblick gegeben, wo die entwickelte Software eingesetzt und wie sie erweitert werden kann.
Außerdem sind im Anhang die Klassenhierarchien der einzelnen Softwarepakete und die
Definition des entwickelten Datenformates angefügt.
4
I Grundlagen
2 Macromedia Flash
5
2 Macromedia Flash
Das Softwareunternehmen Macromedia mit Sitz im kalifornischen San Francisco ist der
Entwickler des Flash Formates und wurde 1992 durch den Zusammenschluß der Firmen MacroMind und Authorware gegründet. Im Jahre 1995 wurde Altsys, der Hersteller
des Grafikprogramms Freehand, übernommen. Die über 1000 Mitarbeiter arbeiten an
Webauthoring Tools und Grafikprogrammen. Sie entwickelten u.a. Dreamweaver, Fireworks, Director und Flash.
Die Entwicklung von Macromedia Flash erfolgte in mehreren Schritten. Im Jahre 1995
entwickelte das Unternehmen FutureWave aus San Diego, Kalifornien, das vektororientierte Illustrationsprogramm SmartSketch und das Plugin Future-Splash-Player, welches
es ermöglichte, SmartSketch Illustrationen im Webbrowser zu betrachten. Darauf aufbauend entwickelte FutureWave 1996 das Animationsprogramm Cel-Animator, welches
dann im Juli 1996 in FutureSplash Animator umbenannt wurde. Ende des Jahres 1996
übernahm dann Macromedia das Unternehmen FutureWave. Anschließend wurden der
FutureSplash Animator und der FutureSplash Player unter dem Namen Flash und Shockwave Flash von Macromedia weiterentwickelt und vertrieben. 1997 erschien die Version
Flash 1 und kurz darauf Flash 2. 1998 wurde ein Java-Player für Shockwave Filme im
Flash 2 Format entwickelt. Das zugehörige Dateiformat wurde innerhalb des Flash Open
”
File Format-Programms “ veröffentlicht. Außerdem gibt es seit 1998 Flash 3, welches
Vektortransparenz und Morphing unterstützt, und den Flash Generator. Der Flash Generator ermöglicht es, Flashfilme dynamisch zu generieren. Hierunter ist allerdings nicht
zu verstehen, daß ein ganzer Film komplett erstellt wird, sondern es werden lediglich Variablen, die in einem mit dem Flash Studio vorbereiteten Film eingesetzt wurden, durch
vorgegebene Objekte ersetzt. So können Variablen über Scripte gesetzt und einzelne Elemente des Filmes verändert werden. Eine Datenbankanbindung ist ebenso möglich wie
die Darstellung statistischer Daten in Diagrammen. Flash 4 erschien 1999 und erweiterte das Flashformat um die Scriptsprache ActionScript. Flash 5 wurde im Sommer 2000
veröffentlicht, wobei in erster Linie die Entwicklungsumgebung Flash Studio weiterentwickelt wurde.
Macromedia Flash ist mittlerweile nicht mehr aus dem Internet wegzudenken. Nachdem
es zu Beginn nur zögerlich eingesetzt wurde, ist die Verbreitung in der letzten Zeit stark
angestiegen und wird von vielen Entwicklern genutzt, um Webseiten interessanter und
bedienungsfreundlicher zu gestalten.
Diese hohe Akzeptanz ist aber nicht allein der Grund, warum für die Visualisierung von
Wetterdaten in dieser Diplomarbeit Macromedia Flash gewählt wurde. Welche Vor- aber
auch Nachteile das Flashformat bietet, wie das Format aufgebaut ist und welche Möglichkeiten existieren, einen Flashfilm zu erstellen, soll in den nächsten Abschnitten näher
erläutert werden.
2.1
Vor- und Nachteile von Macromedia Flash
Durch geschickte Allianzen wurde das Flashplugin mit den Browsern von Netscape und
Microsoft ausgeliefert, so daß eine große Verbreitung ermöglicht wurde. Macromedia
2 Macromedia Flash
6
gibt an, daß im März 2001 mindestens 96% aller Internetnutzer Flashinhalte betrachten
können, das entspricht ca. 334 Millionen Nutzern. Das unabhängige Marktforschungsinstitut NPD Online [NPDO2001] kam bei einer Befragung von 2000 Personen zu diesem
Ergebnis [Macr2001a]:
Verfügbarkeit nach Version
USA
Kanada
Europa
Asien
Südamerika
Flash 2
96.0%
97.1%
96.2%
96.1%
98.2%
Flash 3
94.7%
96.0%
94.6%
93.3%
93.3%
Flash 4
89.5%
92.6%
90.9%
88.3%
92.5%
Flash 5
54.6%
59.0%
64.6%
62.6%
66.4%
Tabelle 2.1: Verfügbarkeit des Macromedia Flash Players (Stand: März 2001)
Tabelle 2.1 zeigt deutlich, daß die 96% Verfügbarkeit des Flash Players, die Macromedia
angibt, lediglich bei Filmen der Version 2 erreicht wird. Zwischen Version 2 und Version 3 gibt es erhebliche Unterschiede. So wurden bei der dritten Version Vektortransparenz, Morphing und Actions eingeführt. In der vierten Version wurde Flash um die
Skriptsprache ActionScript erweitert, bietet zusätzlich Texteingabefelder zur Erstellung
von Formularen und unterstützt das MP3 Format. Die Neuerungen von Flash 5 bestehen
lediglich in der Anpassung der ActionScript-Syntax an JavaScript und der Erweiterung
um einen Debugger.
Die Filme sind zwar bedingt abwärtskompatibel, so daß ein Film der Version 5 mit einem Player der Version 4 abgespielt werden kann, dies ist aber nicht immer möglich.
Will man also viele Internetnutzer erreichen ist es am sinnvollsten Flash 4 einzusetzen,
da so die wichtigsten Features von Macromedia Flash gut ausgenutzt werden können und
immerhin noch im Schnitt 90.7% aller Internetnutzer, daß entspricht ca. 315 Millionen,
wenn von 348 Millionen Internetnutzern ausgegangen wird [IDC2001], diese Flashinhalte betrachten können.
Dieses Anwenderpotential ist ein großer Vorteil für Macromedia. Ohne dieses Potential
würden die Webdeveloper nicht auf Macromedia Flash zurückgreifen, um ihre Webseiten
grafisch aufzuwerten. Aber auch das Herunterladen des Players stellt kein Problem dar,
da dieser lediglich ca. 200 kB groß ist. Die große Verbreitung und die leichte Verfügbarkeit sind nur zwei Vorteile, die Flash bietet. Daneben gibt es noch wesentlich mehr Vorteile, auf die im Folgenden näher eingegangen werden soll.
Das Anbieten von Animationen auf Webseiten stellte oft ein großes Problem für den
Entwickler dar. Zur Lösung dieses Problems wurden meist animierte GIF Bilder eingesetzt. Mit Flash ist ein Format entwickelt worden, mit dem umfangreiche Animationen
in hoher Qualität möglich sind und das es erlaubt, mit dem Benutzer in Interaktion zu
treten. Dementsprechend können Knöpfe, bzw. sensitive Flächen oder Textfelder eingebunden werden und die Eingaben des Benutzers lassen sich per ActionScript, eine von
Macromedia entwickelte umfangreiche Scriptsprache für das Flashformat, validieren und
auswerten. Dies sind Möglichkeiten, die ohne das Flashformat nicht vorhanden waren.
Dabei stellt sich aber die Frage, inwieweit dies Auswirkungen auf die Dateigröße oder
die Darstellungsqualität hat.
Das Produkt Flash bzw. das Format Shockwave Flash (SWF; sprich: swiff “) ist ein
”
2 Macromedia Flash
7
zweidimensionales Vektorgrafikformat, d.h. die Informationen werden nicht Pixel für
Pixel abgespeichert, sondern über die mathematische Beschreibung von geometrischen
Objekten. So wird z.B. ein Kreis lediglich über die Koordinaten seines Mittelpunktes und
des Radius definiert oder eine Linie über ihren Start- und Endpunkt. Hinzu kommen noch
die Angaben über Liniendicke und -farbe sowie die gegebenenfalls notwendige Füllfarbe des Kreises. Hierdurch werden die Dateien sehr viel kleiner als Pixelgrafiken, wo
jedes einzelne Pixel mit seiner Farbinformation abgespeichert werden muß. Die beiden
Grafikformate sind in der Abbildung 2.1 gegenübergestellt.
(a) Pixelgrafik
(b) Vektorgrafik
Abbildung 2.1: Pixel- und Vektorgrafik im Vergleich
Als nachteilig muß hier aber erwähnt werden, daß diese Platzersparnis nicht bei fotorealistischen Bildern erreicht werden kann. Vektorgrafiken sind für derartige Bilder ungeeignet, da für jede kleine Fläche mit einem bestimmten Farbwert ein entsprechendes
Objekt angelegt werden muß, was den Speicherbedarf stark in die Höhe treibt, wenn
zusammenhängende Flächen gleicher Farbe auf die Größe eines Pixels schrumpfen. Vektorgrafiken sind also meist nur für Zeichnungen geeignet.
Die Platzersparnis wird bei Flash nicht nur durch das Vektorgrafikformat erreicht, sondern auch dadurch, daß es sich um ein komprimiertes Binärformat handelt. Hierdurch
wird der Platzbedarf noch zusätzlich reduziert, da die Daten bitweise gepackt werden
(siehe Kapitel 2.3). Eine weitere Verringerung der Dateigröße wird durch die Unterstützung des besonders platzsparenden MP3-Formates bei der Einbindung von Tonmaterial erreicht.
Neben der Platzersparnis gibt es noch den Vorteil der freien Skalierbarkeit der Flashfilme.
Ein Betrachter kann in einen Flashfilm bis zu fünf mal hineinzoomen, ohne daß ein Qualitätsverlust eintritt (Abbildung 2.2b). Dies liegt daran, daß mathematische Koordinaten
als Grundlage dienen, wodurch das Bild in einer höheren Zoomstufe neu berechnet werden kann. Pixelgrafiken hingegen sind für eine bestimmte Auflösung erstellt worden, so
daß beim Vergrößern einer solchen Grafik immer der Treppeneffekt eintritt: Es werden
die einzelnen Pixel – die kleinste auf dem Monitor darzustellende Einheit – sichtbar (siehe Abbildung 2.2a).
2 Macromedia Flash
8
(a) Pixelgrafik
(b) Vektorgrafik
Abbildung 2.2: Vergrößerung einer Pixel- und einer Vektorgrafik
Bei einer Vektorgrafik, wie bei Macromedia Flash, wird die Grafik hingegen nach dem
Hineinzoomen neu errechnet und ist daher immer von hoher Qualität. Dadurch ist es
möglich, einen Ausschnitt einer Vektorgrafik in der bestmöglichen Qualität auf dem Bildschirm oder dem Drucker darzustellen.
Die Wiedergabe eines Flashfilmes erfolgt, wie bereits erwähnt, durch den Flash Player.
Dieser ist für fast alle Betriebssysteme implementiert und deswegen nahezu völlig plattformunabhängig. Ein weiterer positiver Nebeneffekt ist die identische Darstellung von
Flashfilmen in verschiedenen Browsern unter unterschiedlichen Betriebssystemen, was
mit HTML7 praktisch nicht möglich ist. Ein Webdesigner hat die volle Kontrolle über die
Visualisierung des Flashfilmes.
Ein wichtiger Punkt für den Einsatz im Internetbereich ist die Streambarkeit des Flashfilmes. Streaming bedeutet, daß die Datei schon verarbeitet bzw. ausgewertet werden
kann, obwohl sie noch nicht komplett übertragen wurde. Bei zu geringer Bandbreite kann
so trotzdem noch ein recht flüssiger Ablauf erreicht werden und der Nutzer muß nicht
erst abwarten, bis der komplette Film geladen ist, um etwas zu sehen. Wie die Streambarkeit erreicht wird, ist in Kapitel 2.3 näher beschrieben.
Dank dieser Vorteile und der geschickten Marketingpolitik konnte sich Flash im Webbereich durchsetzen. Zunächst wurden lediglich kleine Buttons oder Navigationsmenues
mit Flash entwickelt, aber nach und nach sogar ganze Webseiten.
Neben diesen Vorteilen gibt es allerdings auch einige Nachteile. Wie bereits erwähnt,
werden Flashfilme erst auf der Clientseite gerendert, also für das Ausgabemedium in Pixel umgerechnet, wofür Rechenleistung erforderlich ist. Ein aufwendiger Flashfilm kann
einen Rechner überfordern, so daß die Darstellung nur mit einer geringen Bildwiederholrate vonstatten gehen kann, sie wird ruckeln oder Unterbrechungen aufweisen. Die Bedeutung dieses Nachteiles hat in den letzten Jahren wegen der immer weiter steigenden
Rechenleistung stark abgenommen und wird dies noch in Zukunft tun. Ein weiterer Negativaspekt ist die fehlende Möglichkeit Urheberschutz zu gewährleisten. Es existiert zwar
ein Mechanismus, welcher das nachträgliche Ändern und Importieren des Flashfilmes in
Flash Studio zu verhindern versucht, dieser kann aber ohne Probleme ausgehebelt werden.
Diese Nachteile sind aber gering gegenüber den Vorteilen die Flash bietet. Daher ist Flash
von Macromedia mittlerweile zum defacto Standard im Webdesignbereich geworden.
7
Hyper Text Markup Language
2 Macromedia Flash
2.2
9
Der Flash Standard
Flash ist der Standard für interaktive Vektorgrafiken und Animationen im Web. Web”
designer verwenden Flash zum Erstellen attraktiver, anpaßbarer und extrem kompakter
Navigationsoberflächen, technischer Illustrationen, Langform-Animationen und anderer
faszinierender Effekte für ihre Site“ [Wol2000].
Dieses Zitat stammt von Macromedia selbst. Auch wenn es vielleicht etwas überheblich
klingen mag, ist es dennoch nicht von der Hand zu weisen, daß Flash zum Design von
Webseiten dazugehört. Was hat es nun mit dem Standard Flash auf sich?
Die noch unvollständige und teilweise fehlerhafte Dokumentation des Dateiformates löste
Macromedia Anfang 2000 durch das SWF Software Development Kit (kurz SWF-SDK),
das kostenlos aus dem Internet zu beziehen ist [Macr2001d]. Darin wird ausführlich
das Dateiformat beschrieben und eigene SWF Dateien lassen sich über mitgelieferte
C++ Klassen erzeugen. Was Macromedia damit bezweckt ist offensichtlich: Durch die
Veröffentlichung des Dateiformates ist es nun auch anderen Anbietern möglich Import
und Export Filter für ihre Softwarepakete zu entwickeln und auch unabhängige Entwickler haben daraufhin Tools erstellt, mit denen es z.B. möglich ist Flashfilme in Bildschirmschoner umzuwandeln oder Texteffekte zu erstellen. So konnte z.B. Olivier Debon einen
Flashplayer für die Linuxversion des Netscape Communicators entwickeln [Debo2001]
und andere Unternehmen implementierten Ausgabefilter für ihre Softwareprodukte, um
Flashfilme abzuspeichern oder integrierten Flash Player, um Flashfilme wiederzugeben.
Adobe unterstützt das SWF-Format, indem die Produkte LiveMotion und GoLive Flashfilme exportieren können. Auch das Unternehmen Corel will das SWF-Format unterstützen. So ist für Corel Draw 9 ein SWF-Exportfilter vorgesehen und in Corel Linux
wird ein Flash Player eingebunden. Zudem hat Macromedia demonstriert, daß Flashfilme
auf Windows CE Systemen, Nokia Handys und auf der Spielekonsole Dreamcast von
Sega abgespielt werden können [Oswf2001]. Viele weitere Unternehmen unterstützen
das SWF-Format, sowohl im Export der Filme als auch bei der Wiedergabe.
Durch diese Entwicklungen und durch die Offenlegung des Dateiformates gelang es Macromedia den Bekanntheitsgrad des Flashformates kontinuierlich zu steigern. Das SWFFormat wurde aber nie von einer unabhängigen Institution standardisiert und die Formatspezifikationen werden weiterhin nur von Macromedia festgelegt. Nur durch die gute
Marketingpolitik wurde das SWF-Format so bekannt und populär, wodurch Macromedia
eine Quasistandardisierung erreicht hat.
Was macht aber den Erfolg des Dateiformates aus? Hierzu soll der interne Aufbau des
Formates näher betrachtet werden, da es für diese Arbeit von entscheidender Bedeutung
ist. Deshalb wird in den nächsten Kapiteln näher auf die Dateistruktur und das SWF-SDK
im Besonderen eingegangen.
2.3
Das Shockwave Flash (SWF) Dateiformat
Um dem Gebrauch im World Wide Web zu genügen und eine ausreichende Funktionalität
und Designmöglichkeit zu bieten, muß das SWF-Format unterschiedliche Anforderungen
erfüllen.
2 Macromedia Flash
10
Im wesentlichen gibt es sieben Punkte, die das SWF-Format von Macromedia entscheidend prägen [Oswf2001]:
1. Bildschirmdarstellung Das SWF Format ist in erster Linie für die Bildschirmdarstellung optimiert. Deshalb werden Antialiasing8 , schnelles Rendern9 und Animationen unterstützt.
2. Erweiterbarkeit Da das SWF-Format tagbasiert ist, können ohne weiteres neue Tags
mit neuen Features hinzugefügt werden.
3. Netzwerkübertragung Die Dateien sollen auch bei stark limitierter oder schwankender Bandbreite gut übertragbar sein. Daher ist das SWF Format komprimiert und
durch den Aufbau wird die Streambarkeit der Datei gewährleistet.
4. Einfachheit Durch die Einfachheit des SWF-Formates kann der Flash Player sehr
klein gehalten und dadurch einfach auf verschiedene Betriebssysteme portiert werden. Zusätzlich wird so erreicht, daß der Flash Player nur wenige systemspezifische
Operationen durchführen muß.
5. Dateiunabhängigkeit Alle nötigen Informationen werden platzsparend in der Flashdatei untergebracht, so daß keine weiteren externen Dateiabhängigkeiten entstehen.
6. Skalierungsfähigkeit Da Flashfilme auf den unterschiedlichsten Rechnern betrachtet
werden, ist es wichtig, daß verschiedene Monitorauflösungen oder Farbanzahlen
sowie unterschiedliche Rechnerleistung berücksichtigt werden.
7. Geschwindigkeit Das SWF Format ist so aufgebaut, daß die Filme schnell gerendert
werden können.
Es wurde bereits erwähnt, daß das SWF Dateiformat ein tagbasiertes Binärformat ist.
Das SWF Dateiformat ist in einzelne Datenblöcke eingeteilt, die Tag, bzw. Record genannt werden. Tags sind einzelne Objekte, die durch Records näher spezifiziert werden.
Records enthalten also alle notwendigen Attribute für ein Tag. Tags kennt man üblicherweise von HTML oder der Extensible Markup Language (XML). Einzelne Codes leiten
einen Abschnitt mit einer bestimmten Bedeutung ein und gegebenenfalls gibt es einen
Code für das Ende eines Tags.
In SWF werden zwei grundlegende Arten von Tags unterschieden. Zum einen gibt es
Definitionstags und zum anderen Kontrolltags. Definitionstags dienen der Erstellung von
Objekten, so können z.B. Kreise, Polygone, Sound oder ähnliches angelegt werden. Mit
den Kontrolltags können die vorher definierten Objekte manipuliert werden, z.B. können
Objekte in ihrer Position, Größe oder Farbe verändert, Sounds gesteuert oder Framewechsel vorgenommen werden. Hierbei ist es besonders wichtig, daß ein Control Tag
sich stets auf ein vorher definiertes Objekt bezieht, da sonst die Streambarkeit nicht mehr
gewährleistet wäre. Die Tagstruktur bietet außerdem den Vorteil, daß das Dateiformat ohne Probleme erweitert werden kann. Sollte sich herausstellen, daß ein bestimmtes Objekt
8
9
Unter Antialiasing versteht man das Glätten von Linien durch Graustufen
Rendern ist der generelle Vorgang, Bilder als Bitmap darzustellen
2 Macromedia Flash
11
oder eine neue Transformation in den Standard aufgenommen werden soll, wird einfach
ein neues Tag zu den bereits bestehenden hinzugefügt. Dieser Vorgang wird bei den verschiedenen Flash Versionen deutlich. Es wurden stets weitere Tags hinzugefügt und die
alten Tags blieben bestehen, wodurch eine bedingte Abwärtskompatibilität erreicht wurde. Ein Flash Player der Version vier kann einen Film im Flash 5 Format einlesen und
wiedergeben, da unbekannte Tags einfach übersprungen werden, dies ist aber nicht immer erfolgreich. Werden wesentliche Filmbestandteile dabei übersprungen, kann es im
schlimmsten Fall zu einem Absturz des Flash Players kommen. Dies tritt vor allem dann
ein, wenn in den Film Flash 5 spezifische Script Elemente eingebunden werden und ein
derartiger Film mit einem älteren Flash Player betrachtet wird.
Es wurde bereits erwähnt, daß das SWF Format ein komprimiertes Binärformat ist. In
SWF werden im wesentlichen die folgenden sechs verschiedenen Strategien angewandt:
1. Wiederverwendung: Jedes Objekt erhält innerhalb eines Filmes eine eindeutige 16
Bit lange numerische ID, anhand derer es referenziert werden kann. So ist es
möglich, daß ein Objekt einmal definiert wird, aber mehrmals in dem Flashfilm
referenziert werden kann. Sollen in einem Film drei Quadrate erscheinen, wird nur
einmal ein Quadrat definiert und die anderen zwei werden durch einen Verweis auf
das erste erstellt. Zusätzlich können verschiedene Farbänderungen oder Transformationen angewandt werden. Dadurch erspart man sich den Speicherplatz für das
mehrmalige Definieren eines Objektes.
2. Komprimierung: Die Inhalte der Tags sind komprimiert. Shapes10 werden mit einem effizienten delta encoding scheme, bei dem nur relative Änderungen abgespeichert werden und nicht die absoluten Werte, komprimiert. Bitmaps werden mit
dem JPEG Algorithmus und Sounds mit verschiedenen Leveln der Adaptive Differential Pulse Code Modulation (ADPCM)11 Kompression verkleinert.
3. Bit Verdichtung: Wenn möglich werden Zahlen in der kleinst möglichen Anzahl von
Bits abgelegt. So werden z.B. Koordinaten in Feldern variabler Länge gehalten,
wobei die Länge durch wenige Bits codiert wird.
4. Standardwerte: Einige Strukturen, wie z.B. Matrizen oder Farbtransformationen, enthalten Matrixelemente, welche häufiger benötigt werden als andere. Ein Beispiel:
Betrachtet man eine Transformationsmatrix, so sind die bedeutendsten Felder die
Translationsfelder12 . Skalierungen und Rotationen sind weit seltener nötig. Falls
ein Skalierungs- oder Rotationsfeld nicht vorhanden ist wird davon ausgegangen,
daß die Skalierung 100% und die Rotation null Grad beträgt.
5. Änderungskodierung: In SWF werden lediglich die relativen Änderungen zwischen
einzelnen Bildern abgespeichert. Ändert sich ein Objekt von einem Frame zum
nächsten nicht, werden keine Informationen für dieses Objekt benötigt. Nur wenn
sich die Lage, Farbe oder Größe ändern werden entsprechende Tags in dieses Frame übernommen.
10
Shapes sind grafische Objekte, wie z.B. Kreise, Rechtecke usw.
Hierunter fällt auch das MP3-Format
12
Werte zur Verschiebung eines Objektes
11
2 Macromedia Flash
12
6. Shape-Datenstruktur: Die Datenstruktur verschiedener Shapes (Kreise, Polygone,
etc.) wird sehr einheitlich gehalten, so daß für verschiedene Shapes die gleiche
Datenbasis gewählt werden kann. Dies minimiert die Anzahl verschiedener Shapestrukturen, was aber keine Einschränkung bezüglich der Vielfalt der grafischen
Objekte nach sich zieht, und erleichtert damit das Rendern. Zusätzlich bewirkt diese Struktur, daß Shapes platzsparender codiert werden können.
Diese sechs Punkte bewirken eine starke Reduzierung der Dateigröße. Es muß aber beachtet werden, daß keine generelle Komprimierung der gesamten Datei vorgenommen
wird. Die Komprimierung bezieht sich immer nur auf einzelne Tags. Dies ermöglicht,
daß der Flash Player die Datei direkt verarbeiten kann, ohne erst eine Dekomprimierung
vorzunehmen, wodurch ein Geschwindigkeitsvorteil erzielt wird. Bitmaps oder Sounds
können je nach Bedarf dekomprimiert werden.
Das Dateiformat ist aber nicht nur so angelegt, daß es besonders platzsparend ist, sondern
es wurde auch für eine hochwertige Darstellungsqualität optimiert. Das SWF Koordinatensystem wurde so definiert, daß eine Einheit 1/1440 inch entspricht. Wenn man ein typisches Wiedergabegerät mit 72dpi13 betrachtet, wäre dies äquivalent zu einem zwanzigstel Pixel. Eine derartige Einheit wird TWIP genannt. Wird ein Flashfilm in der höchsten
Zoomstufe wiedergegeben entspricht ein TWIP genau einem Pixel. Durch die Wahl dieser Einheit ist es möglich eine hohe Auflösung beim Hineinzoomen zu erreichen und
Objekte genau zu platzieren.
2.3.1
Datentypen in SWF
Damit Flashfilme möglichst platzsparend sind, wird für Koordinaten-, Transformationsoder andere Werte immer der kleinstmögliche Datentyp genutzt. Die wichtigsten Datentypen im SWF-Format sind in der Tabelle 2.2 aufgeführt.
Datentyp
SI8
SI16
SI8[n]
SI16[n]
UI8
UI16
UI32
UI8[n]
UI16[n]
SB[nBits]
UB[nBits]
Erläuterung
Signed 8-bit Integer Wert
Signed 16-bit Integer Wert
Array mit n “ Signed 8-bit Integer Werten
”
Array mit n “ Signed 16-bit Integer Werten
”
Unsigned 8-bit Integer Wert
Unsigned 16-bit Integer Wert
Unsigned 32-bit Integer Wert
Array mit n “ Unsigned 8-bit Integer Werten
”
Array mit n “ Unsigned 16-bit Integer Werten
”
Signed Bit Wert ( n “ Bits werden genutzt)
”
Unsigned Bit Wert ( n “ Bits werden genutzt)
”
Tabelle 2.2: Die wichtigsten Datentypen im SWF-Fileformat
13
dpi=Dots per Inch
2 Macromedia Flash
13
Aus diesen Werten werden weitere Datentypen abgeleitet, z.B. der Datentyp RECT, der
exemplarisch näher aufgeschlüsselt werden soll. Der Datentyp RECT enthält die Koordinaten für ein Rechteck (Tabelle 2.3) und wird unterschiedlich genutzt, z.B. als Definition
für die Größe eines Flashfilmes oder zur Definition eines Rechteckobjektes im Film.
Datenfeld
NBits
Xmin
Ymin
Xmax
Ymax
Datentyp
UB[5] = nBits
SB[nBits]
SB[nBits]
SB[nBits]
SB[nBits]
Erklärung
Anzahl Bits in den folgenden Feldern
X-Minimum Position des Rechtecks
Y-Minimum Position des Rechtecks
X-Maximum Position des Rechtecks
Y-Maximum Position des Rechtecks
Tabelle 2.3: Der RECT Datentyp im SWF-Fileformat
In der Datenstruktur RECT ist zunächst ein Feld von fünf Unsigned Bits enthalten. In
diesem Feld wird festgelegt, wieviele Bits für die weiteren Datenfelder benötigt werden.
So ist es möglich, die Bitlänge für unterschiedlich große Koordinaten variabel zu halten
ohne spezielle Markierungen einzuführen, wann der nächste Koordinatenwert beginnt.
Dies wird einfach über die vorher definierte Länge ermittelt.
So wie die RECT-Struktur werden auch weitere Strukturen definiert, auf die hier nicht
näher eingegangen werden soll, weitere Informationen sind unter [Macr2001] zu erhalten.
Eine SWF Datei wird immer mit einem Dateiheader, der die Version des Flashfilmes, die
Gesamtlänge der Datei, die Bildwiederholrate, Ausmaße des Filmes und die Gesamtanzahl der Schlüsselbilder enthält, eingeleitet (Tabelle 2.4).
Inhalt
Signature
Signature
Signature
Version
FileLength
FrameSize
FrameRate
FrameCount
Datentyp
UI8
UI8
UI8
UI8
UI32
RECT
UI16
UI16
Erläuterung
Immer ’F’
Immer ’W’
Immer ’S’
Flashversion (z.B. 0x04 für SWF4)
Länge der gesamten Datei
Framegröße in TWIPS (siehe auch Tabelle 2.3)
Bildwiederholrate: Frames pro Sekunde
Anzahl der Frames im Flashfilm
Tabelle 2.4: Dateiheader eines Flashfilmes
Nach diesem Header folgen dann die weiteren Tags, die Objekte definieren oder verändern.
Eine schematische Abbildung der Dateistruktur ist in der Grafik 2.3 zu sehen.
Dateiheader Tag
Tag
Tag
Tag
...
EndTag
Abbildung 2.3: Dateistruktur einer SWF-Datei
2 Macromedia Flash
14
Im SWF-Format wird das Ende eines Tags nicht explizit angegeben, sondern durch die
Längenangabe des Gesamttags ermittelt, die im sogenannten Recordheader codiert wird.
Es werden zwei Recordheader unterschieden, welche für unterschiedliche Recordlängen
zuständig sind. Der Basis Recordheader enthält als erstes auf einer Länge von zehn Unsigned Bits eine ID, das ist eine eindeutige Zeichenkette zur Identifikation des Objektes,
und in den nächsten sechs Unsigned Bits die Gesamtlänge des Tags. Dieser Header ist
für Tags bestimmt, deren Gesamtlänge 63 Byte nicht überschreitet (Tabelle 2.5).
Inhalt
Tag
Länge
Datentyp
10 Unsigned Bit
6 Unsigned Bit
Erläuterung
ID des Tags
Länge des ges. Tags
Tabelle 2.5: Recordheader für Tags, die eine Gesamtlänge von 63 Byte nicht überschreiten
Sollte ein Tag länger sein, so wird zwischen der ID und der codierten Länge ein sechsstelliger Unsigned Bit Code mit dem Wert 0x3F eingefügt. Dieser signalisiert, daß die
codierte Länge in diesem Header im Wertebereich einer Unsigned Integer liegen kann
(Tabelle 2.6).
Inhalt
Tag
Kodierung für einen langen
Recordheader
Länge
Datentyp
10 UI
6 UB
Erläuterung
ID des Tags
immer 0x3F
6 UI
Länge des gesamten Tags
Tabelle 2.6: Recordheader für Tags, die eine Gesamtlänge von 63 Byte überschreiten
Die ID des Tags, welche im Recordheader angegeben wird, dient der Ermittlung, welche Art von Objekt folgen wird. Im folgenden soll gezeigt werden, wie eine Action
im SWF-Dateiformat definiert ist. Als erstes wird das DoAction-Tag in der Tabelle 2.7
angegeben.
Inhalt
Header
Datentyp
RECORDHEADER
Actions
ActionEndFlag
ACTIONRECORD[0 oder mehr]
UI8
Erläuterung
Tag-ID=12 (siehe auch die
Tabellen 2.5 und 2.6)
Verschiedene Actions
immer der Wert 0 “
”
Tabelle 2.7: Struktur des DoAction-Tags
In der Tabelle 2.8 wird der Datentyp ACTIONRECORD, der die eigentliche Definition
der Aktion enthält, angegeben. Als Beispiel soll hier das ActionGetURL-Tag vorgestellt
werden, mit dem innerhalb eines Flashfilmes zu einer Webseite gesprungen werden kann.
2 Macromedia Flash
Inhalt
ActionCode
Length
URLString
TargetString
Datentyp
UI18
UI16
STRING
STRING
15
Erläuterung
Code der Action, hier 0x83 für ActionGetURL
Länge des Records
Adresse zu der gesprungen werden soll
Angabe wo die Adresse dargestellt werden soll
Tabelle 2.8: Struktur des ActionGetURL-Tags
Alle weiteren Definitionen können den Formatspezifikationen [Macr2001] entnommen
werden und sind hier nicht näher aufgeführt, da sie eine sehr komplexe Struktur besitzen
und hier lediglich der grundlegende Aufbau von SWF-Tags dargestellt werden soll.
2.3.2
Darstellung eines Flashfilmes
Bis zur Darstellung eines Grafikobjektes laufen die folgenden Schritte ab:
1. ein Definitionstag definiert das Grafikobjekt
2. das Grafikobjekt erhält eine eindeutige Kennung (ID)
3. die Kennung wird in einer Referenzliste gespeichert
4. ein Kontrolltag referenziert das Objekt über seine Kennung und stellt es auf dem
Bildschirm dar
In diesen Schritten wird deutlich, warum ein Flashfilm streambar ist: Da Kontrolltags
immer nur auf Definitionsblöcke, die weiter vorne in der Datei stehen, Bezug nehmen
dürfen, ist es nicht nötig, auf die Darstellung des Filmes zu warten bis dieser komplett
heruntergeladen ist. Er kann bis zu dem Frame, bis zu dem er bereits aus dem Internet
bezogen wurde, sofort dargestellt werden.
Um die Struktur eines Flashfilmes noch besser zu verstehen, wird ein Flashfilm mit einem
Parser ausgelesen, daß heißt das Binärformat wird in für den Menschen lesbaren Text
umgewandelt. Ein solcher Parser ist unter [Oswf2001] erhältlich.
Das Parsen eines Flashfilmes wird einfach auf der Kommandozeile mit dem Kommando
swfparser Flashdatei.swf
durchgeführt. Ebenfalls auf der Komandozeile findet dann die Ausgabe der geparsten
Datei statt. Bespielhaft soll ein Film geparst werden, welcher ein Quadrat enthält, das
in zwei Schritten um 60 Grad gedreht wird und einen Kreis, der sich nicht ändert (Abbildung 7.5). In dem zugehörigen Quellcode 2.1, das Ergebnis des Parsens, sind die im
Flashfilm enthaltenen Informationen näher aufgeschlüsselt. Als erstes sind die Informationen des Dateiheaders angegeben. Der Film liegt in der Version 4 vor und ist insgesamt
258 kB groß. Die Größe des Filmes in Pixel beträgt 300*300, die Framerate beträgt drei
Frames pro Sekunde und insgesamt sind drei Schlüsselbilder in dem Film enthalten.
2 Macromedia Flash
16
Abbildung 2.4: Snapshot eines Flashfilmes
***** Dumping SWF File Information *****
----- Reading the file header ----FWS
File version
4
File size
258
Movie width
300
Movie height
300
Frame rate
3
Frame count
3
----- Reading movie details ----<----- dumping frame 0
file offset 29
tagLen
54: tagDefineShape3
tagid 1
tagLen
5: tagPlaceObject2
flags 2
tagLen
91: tagDefineShape3
tagid 2
tagLen
5: tagPlaceObject2
flags 2
tagLen
0: tagShowFrame
----->
depth 0
tag 1
depth 3
tag 2
<----- dumping frame 1
file offset 198
tagLen
4: tagRemoveObject
tagid 1
tagLen
19: tagPlaceObject2
flags 6
tagLen
0: tagShowFrame
----->
depth 0
depth 1
tag 1
<----- dumping frame 2
file offset 227
tagLen
4: tagRemoveObject
tagid 1
tagLen
19: tagPlaceObject2
flags 6
tagLen
0: tagShowFrame
----->
depth 1
depth 2
tag 1
<----- dumping frame 3
tagLen
0: tagEnd
file offset 256
----->
***** Finished Dumping SWF File Information *****
Quellcode 2.1: Durch Parsen sichtbar gemachte Tags des SWF Filmes (Snapshot in Abb. 7.5)
2 Macromedia Flash
17
Es folgen dann die Definitions- und Kontrolltags des Filmes.
Im ersten Frame (Frame 0) werden die beiden Objekte definiert und im Film plaziert.
Das Tag tagShowFrame gibt an, daß dieses Frame vollständig ist und dargestellt werden kann. In der Ausgabe des Parsers ist jedoch nicht ohne weiteres zu erkennen welches
Objekt welche ID erhält. Bei der Betrachtung der gesamten Datei wird deutlich, daß das
Quadrat die ID 1 und der Kreis die ID 2 erhält, da nur das Quadrat gedreht wird und
daher mehrfach aus dem Film entfernt und wieder eingefügt wird. In den weiteren beiden Frames wird das Quadrat gedreht. Hierzu wird zunächst das Quadrat mit dem Tag
tagRemoveObjekt und der Angabe der Tagid 1 entfernt, wieder neu plaziert und das
Frame wiederum dargestellt. Leider gibt der Parser nicht die Kontrolltags an, die das
Quadrat drehen. Das nächste Frame ist genauso aufgebaut wie das vorherige und im letzten Frame ist lediglich das Tag tagEnd enthalten, welches das Ende des Filmes anzeigt.
Flashfilme sind, wie bereits erwähnt, in binärcodierten und tagbasierten Dateien abgelegt. Ist es nun notwendig alle Tags und Bitcodierungen zu kennen, um eine solche Datei
zu erzeugen? Macromedia hat das SWF Software Development Kit (kurz SWF-SDK)
kostenlos zur Verfügung gestellt. Mit diesem SWF-SDK ist es möglich Flashfilme über
C++ Klassen zu erzeugen. Wie dieses SDK genutzt wird, ist im nächsten Abschnitt beschrieben.
2.4
SWF – SDK
Das Macromedia Flash File Format (SWF) Software Development Kit (SDK) “ – so der
”
volle Titel – ist auf der Webseite von Macromedia kostenlos erhältlich und enthält neben
der Dokumentation des SWF-Dateiformats den in C++ geschriebenen Quellcode, der das
Erzeugen von Flashdateien über eine Programmierschnittstelle ermöglicht.
Die mitgelieferten Klassen sind mit der Einschränkung, daß sie lediglich für die Entwicklung von SWF-exportierenden Programmen genutzt werden, frei zu verwenden.
Macromedia verlangt, daß die unter Verwendung des SDK entwickelten Produkte Flashfilme im korrekten Format abspeichern. Erzeugte SWF-Dateien müssen zum einen fehlerfrei mit einem aktuellen Flash Player abgespielt und zum anderen fehlerfrei in die
grafische Entwicklungsumgebung Flash Studio importiert werden können. Desweiteren
übernimmt Macromedia keine Garantie für die Korrektheit der bereitgestellten Software
[Macr2001d] und wie sich im Verlauf der Arbeit herausstellte sind einige Fehler im SDK
enthalten.
Das zur Zeit verfügbare SWF-SDK stellt über eine Bibliothek von C++-Klassen die komplette Funktionalität bereit, die benötigt wird, um Flash 3 SWF-Dateien zu erzeugen.
Die Klassen der Bibliothek werden in zwei Bereiche unterteilt: den Lowlevel-Manager
und den Highlevel-Manager: Die Lowlevel-Klassen stellen eine sehr genaue Abbildung
des Dateiformates dar. Für jedes Definitions- oder Kontrolltag existiert eine entsprechende Lowlevel-Klasse im SWF-SDK, die von der Klasse FObj (Flash Objekt) abstammt.
Auch alle anderen Records werden als Klassen umgesetzt. Der eigentliche Flashfilm wird
in der Containerklasse FObjCollection erstellt, indem diese Klasse alle erzeugten
FObj Objekte sammelt und schließlich in eine Datei schreibt. Tabelle 2.9 zeigt in welche Gruppen sich die Lowlevel-Klassen einteilen lassen.
2 Macromedia Flash
18
FObjCollection
FObj
Control Tag Classes
Definition Tag Classes
Primitive Data Classes
Helper Classes
- sammelt Tags (FObjs) und schreibt die Datei
- Basisklasse aller Tagklassen
- Kontrolltags (beginnen mit FCT)
- Definitionstags (beginnen mit FDT)
- weitere Records wie Füllstil, Farbe, Linie, ...
- Hilfsklassen für Sound und Pixelgrafiken
Tabelle 2.9: Lowlevelklassen des SWF-SDKs
Die Highlevel-Klassen bieten eine benutzerfreundlichere Schnittstelle zur Erzeugung eines Flashfilmes, da sie nicht alle Tags des SWF Formates abbilden, sondern, auf den
Lowlevelklassen basierend, die Objekte in einem Flashfilm repräsentieren, was eine wesentlich abstraktere Sichtweise als bei den Lowlevel-Klassen ist.
Die Highlevel-Klassen beginnen alle mit dem Präfix HF “ für High Level Flash. Es exi”
stieren Klassen zum Erzeugen von Filmen (HFMovie), Schlüsselbildern (HFFrame),
Grafikobjekten (HFCircle, HFPolygon, ...), Text (HFText, HFEditText) und viele mehr. Allerdings ist die Actionscript-Unterstützung noch sehr mangelhaft. Die Tabelle
2.10 gibt einen Überblick über die Highlevel-Klassen und deren Funktionalität.
HFMovie
HFFrame
HFObject
HFButton
HFFont
HFShape
HFEditText
HFOval
HFCircle
HFPolygon
HFRectangle
HFBitmap
HFText
HFSound
HFAction
HFActionGetURL
HFActionGoToFrame
HFActionPlay
HFActionStop
- Flashfilm
- Schlüsselbild
- Basisklasse aller Film-Elemente
- Schaltknopf
- Schriftart
- Basisklasse aller Grafikobjekte
- Editierbares Textfeld
- Ellipse
- Kreis
- Polygon
- Rechteck
- Rechteck mit Pixelgrafik gefüllt
- Text
- Sound
- ActionScript-Anweisung
- Laden einer Webseite
- Zu einem bestimten Bild im Film springen
- Einen Film abspielen
- Einen Film stoppen
Tabelle 2.10: Highlevelklassen des SWF-SDKs
Die Verwendung der Highlevelklassen soll an einem kleinen Beispiel demonstriert werden: Ein Kreis mit einem radialen Gradientenverlauf als Füllung und ein Rechteck mit
einem linearen Gradientenverlauf werden in einem 300 ∗ 300 Pixel großen Film plaziert.
Das Rechteck wird dann in zwei Schritten um 60 Grad gedreht. Danach beginnt der Flashfilm wieder von vorne. In Abb 7.5 auf Seite 97 ist ein Snapshot des Flashfilmes zu sehen
und die zugehörige Implementation ist im Quellcode 2.2 angeführt.
2 Macromedia Flash
19
#include <HF3SDK.h>
int main() {
// erzeuge einen neuen Flash-Film
HFMovie movie = HFMovie();
// setze die groesse auf 300*300 Pixel
// Angaben in TWIPS=>6000*6000
movie.SetSize( 6000, 6000 );
// drei Bilder pro Sekunde
movie.SetFrameRate( 3 );
// ein Rechteck (FPIXEL = 20, Umrechnung Pixel nach TWIPS)
HFRectangle* rect =
new HFRectangle( 20*FPIXEL,20*FPIXEL, 120*FPIXEL,120*FPIXEL );
// ein Kreis
HFCircle* circle =
new HFCircle( 80*FPIXEL,50*FPIXEL,40*FPIXEL );
// setze die Fuellfarben
rect->SetLinearFill( Blue_RGBA, Black_RGBA );
circle->SetRadialFill( Yellow_RGBA, Violet_RGBA );
// platziere Objekte uebereinander
rect->SetDepth( 1 ); circle->SetDepth( 2 );
// fuege Objekte zu erstem Schluesselbild hinzu
movie.Frame( 0 )->AddObject( rect );
movie.Frame( 0 )->AddObject( circle );
// rect dreht sich um den eigenen Mittelpunkt
movie.Frame( 1 )->RemoveObject( rect );
rect->Rotate( FloatToFixed( 30.0 ) );
movie.Frame( 1 )->AddObject( rect );
movie.Frame( 2 )->RemoveObject( rect );
rect->Rotate( FloatToFixed( 60.0 ) );
movie.Frame( 2 )->AddObject( rect );
// schreibe die SWF-Datei und beende das Programm
movie.WriteMovie("HFExample.swf");
delete rect; delete circle;
return( 0 );
}
Quellcode 2.2: Implementation zur Erstellung eines Flashfilmes (siehe auch Abb. 7.5, Seite 97)
2 Macromedia Flash
20
Durch das SWF-SDK ist es nun möglich, Flashfilme zu programmieren, völlig losgelöst
vom Flash Studio, mit dem im allgemeinen Flashfilme erstellt werden. Allerdings ist
diese Art der Flashfilmerstellung recht aufwendig, wenn man z.B. bedenkt, daß für eine
Wetterprognose täglich ein Film erstellt werden soll. Jeder Film muß einzeln programmiert werden. Falls sich also in einem Film lediglich die Farbe eines Objektes ändern soll,
muß der Quellcode des Programms geändert und neu kompiliert werden. Dies ist keine
sehr effektive Vorgehensweise, vor allem wenn man bedenkt, daß bei der Entwicklung
eines Flashfilmes häufig Änderungen vorgenommen werden, da z.B. die Größe oder Farbe eines Objektes noch nicht ganz genau den Vorstellungen des Entwicklers entspricht.
Damit die Erstellung eines Flashfilmes auf Basis des SWF-SDKs komfortabler vonstatten geht, wäre es denkbar eine Art Generator14 zu implementieren, der ein Datenformat
einliest und dann zu einem Flashfilm umwandelt. Sollten sich dann Änderungen ergeben,
sind diese nur in der Datei durchzuführen und es ist kein Neukompilieren mehr notwendig. Wie ein derartiger Generator aufgebaut sein kann, wird in Kapitel 6.3 beschrieben.
Da das von Macromedia zur Verfügung gestellte SDK in C++ entwickelt wurde, muß
auch der Generator in C++ entwickelt werden, oder gibt es andere Möglichkeiten?
Wenn ein Entwickler das SDK nutzen möchte, aber nicht in der Programmiersprache
C++ programmieren will, ist dies auf den ersten Blick nicht möglich. Dies ist aber vielleicht wünschenswert, da andere Programmiersprachen für die zu entwickelnde Anwendung besser geeignet sind, so z.B. die Entwicklung in Java, weil Java eine plattformunabhängige, leicht zu erlernende Programmiersprache ist, das Java Development Kit zur
freien Verfügung steht oder dem Entwickler Java persönlich lieber ist als die Entwicklung
in C++. Es wäre also erstrebenswert das SWF-SDK für Java zugänglich zu machen, was
mit dem Java Native Interface möglich ist. Dies ist ein Bestandteil dieser Diplomarbeit
und wird in den Kapiteln 4 und 6.1 beschrieben.
14
damit ist nicht der Flashgenerator von Macromedia gemeint
3 eXtensible Markup Language
21
3 eXtensible Markup Language
In dieser Arbeit ist das Hauptziel Daten mit Macromedia Flash zu visualisieren. Konkret
soll es sich dabei um zeit- und raumbezogene Daten handeln. Das Ausgabeformat Macromedia Flash ist somit vorgegeben, das Format der Eingabedaten hingegen nicht.
Es ist nicht vorgesehen verschiedene Importfilter zu entwickeln, welche unterschiedliche
Daten einlesen und so aufbereiten, daß sie für diese Arbeit von Nutzen sind. Vielmehr
soll ein Schnittstellenformat entwickelt werden, aus dem ein Flashfilm generiert werden
kann und in das beliebige Daten konvertiert werden können. Damit die Umwandlung
in das Flashformat zügig und einfach vonstatten gehen kann soll dieses Datenformat
stark an das Flashformat angelehnt sein und dementsprechend die Tagstruktur und die
Erweiterbarkeit des Flashformates wiederspiegeln. Wenn nun zusätzlich noch das Flash
SDK von Macromedia berücksichtigt wird, auf welchem der Generator aufsetzt, erweist
es sich als noch sinnvoller, die Struktur des SDKs und dessen Objekte in dieses Dateiformat zu übernehmen. Ein derartiges Format sollte zudem textbasiert sein, damit es nicht
aufwendig konvertiert werden muß und somit einfach zu erstellen ist. Außerdem ist es
dadurch möglich derartige Dateien direkt per Hand zu schreiben und erstellte Dateien
bereits beim Durchlesen zu verstehen. Eine Datei in diesem Eingabeformat wird dann
von dem Generator eingelesen, welcher anschließend daraus einen Flashfilm erstellt.
Hierdurch sind einige grundlegende Voraussetzungen an das Format gestellt worden, so
daß ein eigenes Schnittstellenformat entwickelt werden kann. Für dieses Schnittstellenformat ist es dann notwendig eigene Lese- und Schreibmodule zu implementieren.
Ein Standard, welcher gewisse Grundregeln bietet, wie z.B. strukturierte Daten in einer
Datei abzulegen sind, ist XML. Was XML bedeutet, welche Ideen mit diesem Standard
verfolgt werden, warum die Implementation eigener Schreib- und Lesemodule bei XML
basierten Daten nicht mehr erforderlich ist und weshalb eine Syntaxprüfung einer solchen
Datei denkbar einfach ist, soll in den nächsten Abschnitten erläutert werden.
3.1
Der Standard XML
XML ist die Abkürzung für eXtensible Markup Language. Das W3C15 stellt XML folgendermaßen dar: Die Extensible Markup Language (XML) ist das universelle Format
”
für strukturierte Dokumente und Informationen für das Web. “ [W3C2001a]
In diesem Zitat wird besonders hervorgehoben, daß XML im Internet genutzt werden
soll. Dies resultiert daraus, daß der HTML Standard sich als unzureichend herausgestellt
hat und die Entwicklung von XML dieses Defizit beheben sollte. XML ist aber weit mehr
als ein Nachfolger für HTML, wie im folgenden dargelegt werden soll.
Im Jahre 1996 wurde mit der Entwicklung von XML begonnen und wurde vom W3C
im Jahre 1998 standardisiert. Aber die eigentlichen Wurzeln von XML beginnen schon
wesentlich früher. Vor der Entwicklung von XML gab es bereits die Standard Generalized Markup Language (SGML), die in den frühen achtziger Jahren entwickelt und 1986
15
World Wide Web Consortium http://www.w3c.org
3 eXtensible Markup Language
22
zum ISO16 Standard 8879 wurde [ISO2001]. Außerdem startete 1990 die Entwicklung
von HTML. Was haben aber SGML und HTML mit XML gemeinsam?
SGML ist eine Metasprache zur Definition, Identifikaion und Benutzung der Struktur
und des Inhalts von Dokumenten. In SGML ist man völlig unabhängig vom Ausgabemedium, da keine Formatierungsanweisungen gespeichert sind, also eine völlige Trennung von Form und Inhalt. Erst durch eine nachträgliche Ausgabeaufbereitung kann man
ein und dasselbe Dokument auf Papier ausdrucken oder auf dem Bildschirm darstellen.
Dies geschieht mit der Document Style Semantics and Specif ication Language (kurz:
DSSSL), die 1996 als ISO/IEC-Standard 10179 verabschiedet wurde [BeMi2000]. Diese
Sprache wurde entworfen, um SGML-Dokumente zu verarbeiten und zu formatieren. Sie
besteht im wesentlichen aus zwei Komponenten: Der Transformationssprache (transformation language) und der Stilsprache (style language). Die erstere transformiert beliebige
SGML-Dokumente in ein einheitliches Format und die zweite legt fest wie das Dokument später aussehen soll. Problematisch ist jedoch die Komplexität von SGML, die die
Entwicklung von SGML-Anwendungen kostenintensiv und aufwendig macht und einer
weiten Verbreitung entgegenstand. Wieviele Schritte notwendig sind, um ein SGMLDokument in ein Ausgabeformat zu transformieren wird in Abbildung 3.1 deutlich.
SGML−
Dokument−
instanz
Externe
Dokumentteile,
z.B. Grafiken
SGML−
Deklaration
und DTDs
Parsing
Trans−
formations−
prozeß
Trans−
formations−
Spezifikation
SGML−
Dokument
Stil−
Spezifikation
Formatierungs−
Prozeß
RTF
PostScript
...
PDF
Abbildung 3.1: Verarbeitungsprozeß für SGML
16
ISO ist die International Standard Organsation
3 eXtensible Markup Language
23
Die Hypertext Markup Language (HTML) ist eine Auszeichnungssprache, die 1989 von
Tim Berners-Lee [Lee2000] am Forschungszentrum der Europäischen Organisation für
Kernforschung (CERN) entwickelt wurde, um wissenschaftliche Dokumente, mit der
Möglichkeit auf andere Dokumente zu verweisen, im Internet zu verschicken [BeMi2000].
HTML ist eine einfache Sprache, mit der Informationen im Internet präsentiert werden
können. Allerdings wurde der HTML-Standard im Laufe der Zeit durch zunehmende
Komerzialisierung des Internets immer weiter verwässert. HTML wird nicht mehr nur zur
Präsentation wissenschaftlicher Arbeiten genutzt, sondern dient als Basis-Technologie
für E-Commerce, Computer based Trainings und vieles mehr. Dies hatte zur Folge, daß
immer mehr Gestaltungsmöglichkeiten geschaffen werden mußten, die das Tagset von
HTML stark erweiterten, und dennoch reichten die Möglichkeiten immer noch nicht aus.
So wurden 1996 die Cascading Style Sheets (kurz: CSS) eingeführt. Damit ist es möglich,
das Basisdokument und die Art wie es formatiert werden soll, voneinander getrennt zu
halten. Außerdem erlauben CSS Web-Autoren, ein Style Sheet für mehrere HTML Seiten zu nutzen und so für ein einheitliches Aussehen der Webseiten (Corporate Identity)
zu sorgen.
Ein weiterer wesentlicher Punkt ist der Browserkrieg “ von Microsoft und Netscape.
”
Sowohl Netscape als auch Microsoft haben eigene HTML-Tags, die nicht zum Standard
zählen, in ihre Browser integriert. Hierdurch sollte erreicht werden, daß Webseiten speziell für einen bestimmten Browser optimiert werden und sich der potentielle Betrachter
dieser Seite dann für diesen Browser entscheiden wird, damit der eigene Marktanteil
steigt. Dies führte aber letztendlich nur dazu, daß der HTML Standard immer weiter unterlaufen wurde und nicht zu einer Steigerung des Marktanteils. Die Webseitenentwickler
gingen nämlich dazu über, die Seiten für beide Browser zu optimieren, wodurch sich keines der zusätzlichen Tagsets von Netscape bzw. Microsoft durchsetzen konnte, sondern
nebeneinander eine Koexistenz führten. Dieser Kampf um die Marktanteile zog noch ein
weiteres Problem nach sich.
Da die Webseiten häufig fehlerhaft geschrieben wurden (fehlende schließende Tags, keine streng geschachtelte Ordnung der Tags), versuchten Netscape und Microsoft ihre
Browser möglichst resistent gegenüber Fehlern zu machen und korrigierten schlechte
Formulierungen im HTML Code. So war es dann nicht mehr notwendig, sich bei der
Entwicklung einer Webseite an strenge Vorgaben zu halten.
SGML-Anwendungen sind also in ihrer Entwicklung viel zu komplex und zu kostenintensiv und HTML ist bezüglich der Gestaltungsmöglichkeiten, der Erweiterbarkeit und
der Verwässerung des Standards nicht mehr ausreichend.
Eine Lösung dieses Dilemmas könnte die Extensible Markup Language sein. XML ist
eine Teilmenge von SGML und kann mit DSSSL, CSS Level 217 oder aber mit der eXtensible Stylesheet Language (XSL) in ein beliebiges Ausgabeformat umgesetzt werden.
Der Zusammenhang zwischen SGML, XML, HTML und deren Styleformaten ist in Abbildung 3.2 auf Seite 24 dargestellt.
Bei der Entwicklung von XML wurden die besten Teile von SGML, gepaart mit der Erfahrung mit HTML, genommen. Es wurde ein Standard geschaffen, der ebenso mächtig
ist wie SGML, aber wensentlich restriktiver und einfacher in der Handhabung.
17
Eine Erweiterung von CSS aus dem Jahre 1998, die zusätzlich systemspezifische Vorlagen, aus dem
Internet zu ladende Fonts und Positionierungsmöglichkeiten bietet.
3 eXtensible Markup Language
24
SGML
Teilmenge von
DSSSL
Teil von
XSL
XML
CSS
Teilmenge von
HTML
Abbildung 3.2: Zusammenhang zwischen SGML, XML, HTML, CSS und DSSSL
Um ein Gefühl dafür zu bekommen, was man unter XML versteht, sollen die wichtigsten
Punkte aufgezählt werden, die XML prägen:
Mit XML werden strukturierte Daten in ein Textfile geschrieben: Oft werden strukturierte Daten erzeugt, z.B. Adressbücher, Konfigurationsparameter, Programmausgaben, technische Zeichnungen, usw.. Diese Daten können sowohl binär als
auch in lesbarem Code abgelegt werden. Letzteres bietet den Vorteil, daß ohne das
erforderliche Programm die Datei angeschaut werden kann. XML bietet eine Anzahl von Regeln oder auch Konventionen, um solche Daten in strukturierter Form
textuell abzulegen. Dies geschieht in einer Weise, die es Programmen erlaubt, solche Dateien einfach zu generieren und zu lesen. Die Regeln sind eindeutig und
verhindern verschiedene Probleme, wie z.B. mangelnde Erweiterbarkeit oder Plattformabhängigkeit.
Der Entwurf von XML muß formal und präzise sein: Eine XML-Anwendung soll genau vorschreiben, welche Elemente an welcher Stelle stehen dürfen. Hierbei darf es
keine Ausnahme geben. Dadurch ist eine XML-Anwendung, die einer bestimmten
Definition entspricht, immer auf die gleiche Weise zu verarbeiten.
XML-Dokumente sollen leicht zu verarbeiten sein: Da XML-Anwendungen sehr restriktiv sind, ist es möglich standardisierte Schreib- und Lesemodule zu entwickeln.
So müssen keine Module für das aufwendige Parsen eines Dokumentes entwickelt
werden.
XML ist tagbasiert: Wie auch bei HTML werden in XML sogenannte Tags mit Attributen genutzt. Ein Tag ist ein durch < und > eingeklammertes Wort. Unter Attributen
versteht man die Zuordnung name = wert. In HTML werden die Tags genutzt,
um anzugeben, wie der Text zwischen den Tags im Browser formatiert werden
soll. In XML dienen die Tags nur dazu, den Text zu strukturieren und in kleine
Abschnitte einzuteilen. Die Interpretation der einzelnen Tags wird dann anderen
Anwendungen überlassen. Ein weiterer Unterschied ist, daß XML wesentlich mehr
reglementiert ist als HTML. In HTML ist es möglich, daß die Attributwerte nicht
in Anführungsstriche gesetzt sind. Dies ist in XML nicht zulässig. XML ist also
wesentlich reiner und sauberer als HTML.
3 eXtensible Markup Language
3.2
25
Einsatzgebiete und Anwendung von XML
Eine XML basierte Auszeichnungssprache wird als XML Anwendung bezeichnet, dies
ist eine Anwendung von XML auf ein spezielles Fachgebiet, wie z.B. Mathematik, Chemie, Literatur oder Multimedia. XML-Anwendungen werden in vielen Bereichen eingesetzt [Beck2000]:
• XML kann als Basisformat verwendet werden, aus dem sich die für die verschiedenen Zwecke und Ausgabemedien passenden Darstellungen einfach generieren
lassen (etwa als Online-Hypertext oder als Papierversion).
• Mittels XML können jegliche strukturierte Daten in eine textuelle Form gebracht
werden, wodurch proprietäre Formate ersetzt werden könnten und so der Austausch zwischen verschiedenen Anwendungen erleichtert wird. Allerdings wird
dadurch der eigentliche Inhalt durch das Einfügen von Tags stark aufgebläht.
• XML erleichtert die Einführung neuer Dokumentversionen, indem es die Verwaltung von abwärtskompatiblen Daten ermöglicht, d.h. von neueren Programmen
erzeugte Dateien können auch von älteren Programmen verarbeitet werden.
Eine der ersten XML-Anwendungen ist die Chemical Markup Language (CML) von
Peter Murray-Rust [Har2000]. CML wurde ursprünglich als SGML-Anwendung entwickelt und dann in XML übertragen. Mit CML werden komplexe chemische Moleküle
in einfacher Form beschrieben. So ist sie für die Darstellung von Molekülstrukturen, Kristallographie, Veröffentlichungen, chemische Datenbanken und vieles mehr verwendbar.
Der Vorteil von CML gegenüber den traditionellen Verfahren ist die einfache Suche in
den Dokumenten und der Versendung der Daten über das Internet, da CML als XMLAnwendung plattformunabhängig ist.
Eine weitere Anwendung ist die Mathematical Markup Language (MathML), die es erlaubt mathematische Gleichungen im Internet verfügbar zu machen. Sicherlich war dies
auch vorher möglich, indem Formeln in Grafiken umgesetzt wurden, aber eine Weiterverarbeitung konnte dann nicht mehr vorgenommen werden. Mit MathML bleibt die Formel
als solche erhalten.
Weitere XML-Anwendungen sind z.B.:
XHTML XHTML könnte HTML in seiner bisherigen Form ablösen. XHTML ist eine
Nachbildung und Erweiterung von HTML 4 auf XML-Basis [W3C2001b].
Channel Definition Format (CDF) Eine von Microsoft entwickelte XML-Anwendung
zur Definition von Channels, welche genutzt werden, um Informationen an Leser
direkt zu übermitteln, die eine Webseite abonniert haben. Dieser Vorgang wird auch
Webcasting genannt.
Synchronized Multimedia Integration Language (SMIL) Eine vom W3C empfohlene XML-Anwendung [W3C2001d] für die Entwicklung TV-ähnlicher “ multime”
dialer Präsentationen für das Internet.
3 eXtensible Markup Language
26
Scalable Vector Graphics (SVG) Unter der Leitung des W3C entwickeltes Vektorgrafikformat [W3C2001e] mit umfangreichen Filtereffekten und Interaktionsmöglichkeiten.
MusicML Von der Connection Factory entwickelte XML-Anwendung für die Notenschrift.
Es gibt noch wesentlich mehr etablierte XML-Anwendungen und weitere werden hinzukommen. Dies sollte lediglich einen kurzen Überblick geben, in wievielen Bereichen
XML-Anwendungen bereits eingesetzt werden.
3.3
Die Document Type Definition – DTD
XML ist eine Meta-Auszeichnungssprache, d.h. eine Sprache mit der eine Auszeichnungssprache beschrieben werden kann. Eine solche Auszeichnungssprache (auch Tagset
genannt) wird mittels einer Document Type Def inition beschrieben. Es wird eine Liste
der Elemente, Attribute, Notationen und Entities, die in einem Dokument enthalten sein
dürfen, sowie deren Beziehung zueinander definiert. Somit wird ein Verfahren bereitgestellt, mit dem es möglich ist, sich auf bestimmte Auszeichnungsstandards zu einigen,
wodurch eine gemeinsame Basis zum Datenaustausch vorhanden ist.
Eine DTD kann zum einen in eine Datei, welche das Dokument enthält, eingebettet oder
aber über eine externe URL18 mit dem Dokument verknüpft werden. Eine solche DTD
ist dann von mehreren Dokumenten gemeinsam nutzbar. In jedem Falle ist es notwendig
die DTD in einem Dokument durch eine Dokumenttyp Deklaration einzubinden. Dies
geschieht in dem Prolog eines Dokumentes nach der XML Deklaration, aber noch vor
dem Root-Tag. Eine XML-Datei kann zum einen die URL der DTD enthalten, die DTD
selbst oder gar beides. Im letzteren Fall besteht die Dokumenttyp Definition aus zwei
Teilen: den internen und den externen Untermengen.
Die DTD ist, wie bereits erwähnt, eine Definition einer Auszeichnungssprache. Von dieser DTD darf nicht abgewichen werden. Dadurch ist sichergestellt, daß keine zusätzlichen
Elemente in einer XML-Anwendung auftreten dürfen. Auch die Reihenfolge der Elemente wird festgeschrieben. Mit diesen vorgegebenen Regeln ist nun eine Validierung einer
XML-Anwendung möglich. Hierbei werden verschiedene Validierungsarten unterschieden. Eine Möglicheit ist die Validierung der Wohlgeformtheit einer XML-Anwendung,
d.h. es wird lediglich die grundlegende Syntax geprüft, also z.B. daß die einzelnen Tags
immer geschlossen oder daß Attributwerte stets in Anführungsstriche gesetzt werden. Eine Validierung anhand einer DTD ist aber ebenso möglich. Hierbei wird dann überprüft,
ob die Anordnung und Schachtelung der einzelnen Tags der DTD entspricht.
Wie eine DTD erstellt wird und welche Elemente dem Entwickler zur Verfügung stehen
wird in den nächsten Abschnitten näher erläutert.
18
Uniform Resource Locator
3 eXtensible Markup Language
3.3.1
27
Dokumentanalyse
Der erste Schritt bei der Erstellung einer DTD sollte die Analyse der vorhandenen Daten sein. In einigen Fällen ist die Information in den Daten gut strukturiert und formal
erfaßbar. In anderen jedoch sind sie eher form- und strukturlos, wie z.B. bei literarischen
Texten. In beiden Fällen muß eine Einteilung der Daten gefunden werden, die sie in eine
strukturierte Form bringt. Ein literarischer Text kann z.B. in einzelne Abschnitte oder
Überschriften eingeteilt werden.
Ziel der Analyse der Daten ist, die wesentlichen Elemente und ihre Attribute herauszufinden. Ein wesentliches Element könnte z.B. eine Person sein, welche als Attribute
einen Namen, Adresse und Geburtsdatum besitzt. Hierbei stellt sich immer die Frage,
was ein Attribut ist und was ein eigenständiges Element. So kann die Adresse durchaus
ein Element sein, wenn sie nicht nur bei der Person eingesetzt wird, sondern auch bei
einem Gebäude oder bei anderen Elementen Verwendung findet wird. Daher ist es oft
nicht eindeutig derartige Elemente und Attribute zu finden, die einerseits die Wirklichkeit gut abbilden, aber dennoch wohlstrukturiert sind. Dieser Vorgang ist sehr gut mit
der Entwicklung einer relationalen Datenbank zu vergleichen, da auch hier Entitäten und
deren Attribute ermittelt werden müssen.
Da eine XML-Anwendung stets hierarchisch aufgebaut ist, ist es auch sinnvoll die DTD
Schritt für Schritt von außen nach innen aufzubauen. Zunächst stellt sich die Frage, welches Wurzelelement gewählt werden soll und welche Elemente innerhalb dieses Wurzelelementes auftreten dürfen. Das Wurzelelement umschließt das gesamte Dokument und
sollte vom Namen her die XML-Anwendung möglichst gut charakterisieren. Nur bei unstrukturierten Daten wird es meist mit ANY oder anderen allgemeinen Bezeichnungen
bezeichnet.
Es ist auch wichtig, sich immer nur die zu strukturierenden Daten vor Augen zu halten
und sich nicht von der zu entwickelnden Software leiten zu lassen, da sonst meist eine
wenig restriktive XML-Anwendung zustande kommen wird und dadurch die eigentliche
Struktur der Daten nicht wiedergegeben wird.
Diese Punkte bewirken, daß die Analyse des Dokumentes häufig wiederholt werden muß
und erst nach mehreren Iterationen die Anordnung der Elemente und Attribute eine gute
Strukturierung der Daten darstellen. Wie aber werden nun diese Elemente und Attribute in einer DTD beschrieben, wie werden Regeln definiert und in welcher Anordnung
dürfen diese auftreten? Dies wird im nächsten Abschnitt näher erläutert.
3.3.2
Die DTD-Syntax
Eine DTD definiert Regeln für eine XML-Anwendung. Jede DTD wird mit einer Anweisung eingeleitet, in der die XML Version festgelegt wird und enthält außerdem immer
den DTD-Namen:
<?xml version="1.0"?>
<!DOCTYPE DTD-Name [
Dokumentdefinition
]>
3 eXtensible Markup Language
28
Innerhalb der eckigen Klammern wird dann die eigentliche Definition der Dokumentsyntax angegeben.
Der grundlegendste Bestandteil einer XML-Anwendung ist das Element. Es darf weitere
oder keine Elemente enthalten. Ein leeres Element wird wie folgt definiert:
<!ELEMENT element_name EMPTY>
Wichtig dabei ist, daß auf die Groß- und Kleinschreibung geachtet wird. So müssen die
Schlüsselwörter ELEMENT und EMPTY groß geschrieben werden. Der Name des Elementes kann frei definiert werden, allerdings muß beachtet werden, daß in der XMLAnwendung der Name genauso geschrieben werden muß wie er definiert wurde. Soll
ein Element weitere Elemente enthalten, wird die Liste der erlaubten Tags anstelle des
Schlüsselwortes EMPTY in runden Klammern eingefügt:
<!ELEMENT element_name (sub_elem)>
In diesem Fall muß das Element sub element genau einmal in element name enthalten sein. Dies kann aber auch variiert werden. Wird z.B. nach der Angabe des Unterelementes nach der schließenden Klammer ein ?“ eingefügt, ist dieses Element op”
tional, es kann also genau einmal oder gar nicht vorkommen. Wenn es gewünscht ist,
daß dieses Element entweder gar nicht oder beliebig oft vorkommen darf, wird anstelle
des ?“ ein *“ eingefügt und falls sub elem mindestens einmal im Element vorkom”
”
men soll, wird ein +“ eingefügt.
”
Soll ein Element mehrere Subelemente enthalten wird dies folgendermaßen definiert:
<!ELEMENT element_name (sub_elem1,sub_elem2,sub_elem3)>
Hierbei müssen die Subelemente in der angegebenen Reihenfolge in dem Element enthalten sein. Auch hier kann das ?“, das +“ oder das *“ genutzt werden, wobei es sich
”
”
”
dann auf alle Subelemente bezieht. Bei einem *“ können die drei Subelemente keinmal,
”
einmal oder beliebig häufig auftreten, allerdings gilt dies immer für alle drei Elemente
in ihrer Gesamtheit. Die Trennung der einzelnen Subelemente durch ein ,“ gibt also
”
die Reihenfolge der Elemente vor und es müssen alle genutzt werden. Soll jedoch eine
Wahlmöglichkeit der einzelnen Subelemente bestehen, wird das ,“ durch ein |“ aus”
”
getauscht. Das Ganze sieht dann folgendermaßen aus:
<!ELEMENT element_name (sub_elem1|sub_elem2|sub_elem3)>
Hier kann also eines der drei Subelemente ausgewählt werden. Auch hier können die
Zeichen ?“, +“, *“ eingesetzt werden, um die Häufigkeit der Subelemente angeben zu
” ” ”
können. Die angegebenen Möglichkeiten können in beliebiger Form kombiniert werden:
<!ELEMENT element_name (sub_elem1?,(sub_elem2|sub_elem3)*)>
In diesem Beispiel enthält das Element element name zunächst ein- oder keinmal das
Subelement sub elem1 und danach entweder das zweite oder das dritte Subelement,
welche keinmal oder beliebig häufig auftreten dürfen.
Nun fehlen noch die zu einem Element gehörigen Attribute. Attribute werden in der
folgenden Form deklariert:
<!ATTLIST element_name attribut_name typ default_wert>
3 eXtensible Markup Language
29
element name ist der Name des Elementes, zu dem das Attribut attribut name
gehört. typ ist der Datentyp des Attributs. Die wichtigsten von XML vorgegebenen Typen sind in Tabelle 3.1 zusammengefaßt. default wert ist der Standardwert des Attributes, der angenommen wird, wenn das Attribut in der XML-Ausprägung weggelassen
wird.
Typ
#CDATA
#PCDATA
Enumerated
ID
IDREF
IDREFS
Bedeutung
Character Data: Beliebige Zeichendaten
Parsed Character Data: Zeichendaten ohne Zeichen der XML-Syntax
Eine Liste von möglichen Werten
Ein im Dokument eindeutiger Name
Der Wert einer vorher definierten ID
Mehrere IDs, durch Leerzeichen getrennt
Tabelle 3.1: Attributtypen in XML
Anstatt einen Standardwert für ein Attribut vorzugeben, gibt es auch die Möglichkeit
Attribute zu erzwingen oder wegzulassen. Hierfür gibt es zwei Schlüsselworte, die eingesetzt werden, wenn kein Standardwert vorgegeben werden kann. #REQUIRED wird
genutzt, wenn ein Attribut zwingend erforderlich ist und #IMPLIED wird eingesetzt,
wenn dieses Attribut auch weggelassen werden kann.
Im Quellcode 3.1 ist als Beispiel eine DTD für die Strukturierung von Personendaten
angegeben und eine mögliche Ausprägung wird im Quellcode 3.2 dargestellt.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE PersListe [
<!ELEMENT PersListe (PERSON)+>
<!ELEMENT PERSON ((VORNAME+), NACHNAME, ADRESSE)>
<!ATTLIST PERSON geschlecht ("m"|"w") #REQUIRED>
<!ELEMENT VORNAME
<!ELEMENT NACHNAME
#PCDATA>
#PCDATA>
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
(STRASSE, HAUSNUMMER, PLZ, ORT)>
#PCDATA>
#PCDATA>
#PCDATA>
#PCDATA>
ADRESSE
STRASSE
HAUSNUMMER
PLZ
ORT
]>
Quellcode 3.1: person.dtd: Einfache DTD zur Strukturierung von Personendaten
3 eXtensible Markup Language
<?xml version=’1.0’ encoding=’UTF-8’?>
<!DOCTYPE PersListe SYSTEM ’person.dtd’>
<PersListe>
<PERSON geschlecht="m">
<VORNAME>Hans</VORNAME>
<VORNAME>Peter></VORNAME>
<VORNAME>Egon</VORNAME>
<NACHNAME>Mustermann</NACHNAME>
<ADRESSE>
<STRASSE>Mustergasse</STRASSE>
<HAUSNUMMER>1</HAUSNUMMER>
<PLZ>0815</PLZ>
<ORT>Musterstadt</ORT>
</ADRESSE>
</PERSON>
<PERSON geschlecht="w">
<VORNAME>Erika</VORNAME>
<NACHNAME>Mustermann</NACHNAME>
<ADRESSE>
<STRASSE>Mustergasse</STRASSE>
<HAUSNUMMER>1</HAUSNUMMER>
<PLZ>0815</PLZ>
<ORT>Musterstadt</ORT>
</ADRESSE>
</PERSON>
<PERSON geschlecht="w">
<VORNAME>Bettina</VORNAME>
<VORNAME>Petra</VORNAME>
<NACHNAME>Mustermann</NACHNAME>
<ADRESSE>
<STRASSE>Mustergasse</STRASSE>
<HAUSNUMMER>1</HAUSNUMMER>
<PLZ>0815</PLZ>
<ORT>Musterstadt</ORT>
</ADRESSE>
</PERSON>
</PersListe>
Quellcode 3.2: Eine mögliche Ausprägung der person.dtd
30
3 eXtensible Markup Language
3.4
31
XML Parser
Der große Vorteil bei der Nutzung von XML-Anwendungen sind die vorhandenen und
auch standardisierten Parser. Diese Parser sind meist frei verfügbar und werden in unterschiedlichen Programmiersprachen angeboten. Einige Parser werden in Kapitel 3.4.2
vorgestellt.
Ein Parser ist ein Programm, das ein Dokument abarbeitet und die enthaltenen Informationen darüberliegenden Schichten der Anwendung in irgendeiner Form zur Verfügung
stellt. Genau dies macht auch ein XML-Parser. Er arbeitet das XML-Dokument durch und
stellt die Informationen (also Elemente, Attribute, usw.) der Applikation zur Verfügung.
XML-Parser lassen sich nach zwei Kriterien unterscheiden: Zum einen ob sie validieren
oder nicht, zum anderen welche Schnittstelle sie zum Zugriff auf das XML-Dokument
anbieten (SAX oder DOM). Validierung eines XML Dokumentes bedeutet die Prüfung
eines Dokumentes auf Wohlgeformtheit oder Gültigkeit. Ein Dokument ist wohlgeformt
(well-formed), wenn es die grundlegenden XML Bedingungen erfüllt, das sind im einzelnen [Beck2000]:
• Alle Elemente müssen wieder geschlossen werden, d.h. zu jedem öffnenden Tag
muß das zugehörige schließende Tag explizit hingeschrieben werden.
• Leere Elemente (z.B. <BR> in HTML) weden durch einen Slash (/) vor der abschließenden spitzen Klammer gekennzeichnet (<BR/> in XML).
• Alle Attributwerte müssen in Anführungszeichen eingeschlossen werden.
Ein Dokument ist gültig (valid), wenn es wohlgeformt ist und sich der Aufbau des Dokumentes zusätzlich an die in der DTD vorgegebenen Regeln hält. Da die Validierung
eines Dokumentes sehr zeitaufwendig ist, sollte genau geprüft werden, ob eine Validierung sinnvoll ist oder nicht. Wenn das zu parsende Dokument maschinell erstellt wurde,
kann davon ausgegangen werden, daß es ein gültiges XML-Dokument ist. In diesem Falle
sollte von einer weiteren Validierung abgesehen werden. Auf den Unterschied zwischen
DOM und SAX Parsern wird in Kapitel 3.4.1 eingegangen.
3.4.1
XML-Parser Aplication Programming Interface
Die einzelnen Parser unterscheiden sich nicht wesentlich in ihrer Anwendung, da sie
meist den vom W3C vorgegebenen Standard XML 1.0 [W3C2001c] implementieren. Es
kann aber sein, daß einige Parser zusätzliche Methoden anbieten oder noch in einem Stadium sind, in dem noch nicht der gesamte vorgegebene Standard implementiert ist. Der
Unterschied zwischen den einzelnen Parsern liegt vielmehr in ihrer Performance. Die
XML-Parser der unterschiedlichen Unternehmen sind bei verschiendenen Aufgaben unterschiedlich schnell. So ist der eine Parser speziell für den Einsatz mit dem Dokument
Objekt Model entwickelt worden, wohingegen ein anderer Parser sehr schnell validieren
kann. Dementsprechend muß die anstehende Aufgabe dahingehend untersucht werden,
3 eXtensible Markup Language
32
welche Arten des Parsens besonders häufig vorkommen und welche vernachlässigbar
sind, um auf diese Art und Weise den richtigen XML-Parser zu finden.
Das Application Programming Interface (API) der XML-Parser ist eine vom W3C standardisierte Schnittstelle [W3C2001a]. Es werden bei einem SAX-Parser immer dieselben
Methoden bei einem Ereignis aufgerufen oder ein DOM wird immer auf dieselbe Art
und Weise manipuliert. Dies hat zur Folge, daß die Parser einfach ausgetauscht werden
können, sofern sich dies als vorteilhaft erweist. Da die Parser bei verschiedenen Aufgaben unterschiedlich schnell sind, ist es somit möglich verschiedene Parser zu testen, um
den schnellsten herauszufinden oder aber weiterentwickelte und fehlerbereinigte Parser
in die eigene Applikation aufzunehmen.
Lediglich bei einigen Punkten ist ein derartiger Austausch des Parsers nicht möglich.
Bei einigen Parsern werden proprietäre Methoden eingebaut, um die Funktionalität zu
erweitern oder die Benutzung des Parsers komfortabler zu gestalten. Werden diese in einer Applikation genutzt, ist man an diesen Parser gebunden. Deshalb ist davon abzuraten
derartige Methoden zu nutzen.
Es gibt zwei verschiedene Schnittstellen über die der Zugriff auf ein XML Dokument
erfolgt. Zum Einen das Document Object Model (DOM) und zum Anderen die Simple
API for XML (SAX).
Document Object Model
Beim Parsen des XML-Dokumentes mit einem DOM-Parser wird ein Baum aufgebaut,
der sogenannte DOM-Tree, wobei das Root-Element des XML-Dokumentes die Wurzel
des DOM-Trees wird. Dieser Baum ist dann im Speicher verfügbar und stellt ein Abbild
des XML-Dokumentes dar. Der zum Quelltext 3.3 gehörende DOM-Tree ist in der Abbildung 3.3 dargestellt.
<artikel>
<titel> Flashweather </titel>
<autor>
<vorname> Ralf </vorname>
<nachname> Kunze </nachname>
</autor>
<inhalt>
text
</inhalt>
</artikel>
Quellcode 3.3: XML Ausprägung
3 eXtensible Markup Language
33
artikel
titel
Flashweather
autor
inhalt
vorname
nachname
Ralf
Kunze
text
Abbildung 3.3: Grafische Darstellung des zum Quellcode 3.3 gehörenden DOM-Trees
Die DOM-API bietet Werkzeuge zum Zugriff auf den Baum und zum Verändern des Baumes und ist daher ideal für interaktive Anwendungen geeignet. Allerdings ist ein DOM
recht langsam und speicherintensiv, da alle Elemente und Attribute als eigene in einem
Baum organisierte Objekte im Speicher abgelegt werden.
Wie man einen DOM-Parser verwendet, wird anhand des Apache Xerces-J [Apac2001b]
gezeigt. Nach dem Import der Parser-Klasse org.apache.xerces.parsers.DOMParser und dem Dokument-Interface org.w3c.dom.Document, das die Wurzel
des DOM-Trees repräsentiert, wird ein XML-Dokument mit den folgenden Zeilen Quellcode in ein DOM überführt:
DOMParser parser = new DOMParser();
parser.parse( XMLDocument_Filename );
Document doc = parser.getDocument();
Von der Dokument-Wurzel doc ausgehend kann nun der Baum traversiert und jedes Element ausgelesen, manipuliert oder die Baumstruktur verändert werden.
Voreingestellt prüft der Xerces-J DOMParser ein XML-Dokument nur auf Wohlgeformtheit. Wenn die Gültigkeit eines Dokumentes überprüft werden soll, muß das entsprechende Feature vor dem Parsen gesetzt werden:
parser.setFeature("http://xml.org/sax/"
+"features/validation",true);
Werden bei der Überprüfung des Dokumentes Fehler gefunden, wird standardmäßig keine Meldung darüber ausgegeben. Ist dies erwünscht kann ein ErrorHandler implementiert werden, der dem Parser folgendermaßen mitgeteilt wird:
parser.setErrorHandler(myErrorHandler);
Im Interface org.xml.sax.ErrorHandler werden die Methodenköpfe vereinbart,
die ein ErrorHandler implementieren muß:
void warning(SAXParseException ex)
//warning
void error(SAXParseException ex)
//recoverable-error
void fatalError(SAXParseException ex) //non-recoverable
3 eXtensible Markup Language
34
Es fällt auf, daß der ErrorHandler für den DOM-Parser aus dem Packet org.xml.sax
stammt. Dies liegt daran, daß ein DOM-Parser auf einem SAX-Parser basiert. Das XMLDokument wird von einem SAX-Parser sequenziell verarbeitet und daraus wird dann,
durch die Ereignisse des Parsers gesteuert, ein DOM-Tree aufgebaut. Die Methoden des
ErrorHandlers werden also durch einen verborgenen SAX-Parser aufgerufen.
In Quellcode 3.4 wird die Zusammenarbeit der genannten Klassen und Methoden dargestellt.
import
import
import
import
import
java.io.IOException;
org.xml.sax.SAXException;
org.xml.sax.SAXParseException;
org.w3c.dom.Document;
org.apache.xerces.parsers.DOMParser;
public class ExampleDomParser {
/** Erstellung eines DOM-Trees mit dem Xerces-J-DOMParser */
public static void main(String args[]) throws IOException,
SAXException {
DOMParser parser = new DOMParser();
parser.setFeature("http://xml.org/sax/"
+"features/validation",true);
//Eigener ErrorHandler als innere Klasse zur Fehlerausgabe
parser.setErrorHandler(
new org.xml.sax.ErrorHandler() {
public void fatalError(SAXParseException err) {
System.out.println("Fatal Error: "+err.getMessage()
+"line "+err.getLineNumber() );
}
public void error (SAXParseException err) {
System.out.println("Error: "+ err.getMessage()
+"line "+err.getLineNumber() );
}
public void warning (SAXParseException err) {
System.out.println("Warning: "+err.getMessage()
+"line "+err.getLineNumber() );
}
}
);
parser.parse(args[0]);
Document root = parser.getDocument();
//Verarbeitung des Dokumentes
}
}
Quellcode 3.4: ExampleDomParser.java: Verwendung eines Xerces-J DOMParsers
3 eXtensible Markup Language
35
Simple API für XML – SAX
Die Simple API für XML (SAX) wurde von Mitgliedern der XML-DEV Mailing List
[Gopr2000] entwickelt. Mit SAX wird ein Dokument eventgesteuert geparst, d.h. es wird
beim Lesen eines Tags ein Ereignis ausgelöst, ebenso bei Fehlern oder Warnungen. Diese
Ereignisse können vom Entwickler individuell behandelt werden, indem ein Eventhandler implementiert und beim Parser registriert wird. Innerhalb des Eventhandlers wird für
die einzelnen Ereignisse eine Methode implementiert, über die man dann auf das Ereignis reagieren kann. Nach der Verarbeitung eines Ereignisses sind alle Informationen dazu
verloren, sofern diese nicht explizit zwischengespeichert wurden. In der Abbildung 3.4
ist der Zusammenhang zwischen einem XML-Dokument und den zugehörigen Ereignissen dargestellt.
XML−Datei
Ereignisse
startDocument
<?xml version="1.0"?>
processingInstruction
<artikel>
startElement: artikel
<para>
HelloWorld!
</para>
</artikel>
startElement: para
characters: HelloWorld!
endElement: para
endElement: artikel
endDocument
Abbildung 3.4: SAX-Parser-Events beim Parsen eines XML-Dokumentes
Wie man einen SAX-Parser verwendet, wird anhand des Apache-Xerces-J Parsers erklärt.
Zunächst muß die Klasse org.apache.xerces.parsers.SAXParser importiert
werden, um mit
SAXParser parser = new SAXParser();
einen neuen SAX-Parser zu instanziieren. Damit die durch das Parsen entstehenden Ereignisse verarbeitet werden können, muß dem Parser ein Handler, der für die Ereignisse
entsprechende Methoden enthält, übergeben werden. Dieser Handler muß von der Klasse org.xml.sax.helpers.DefaultHandler abstammen. Indem die Methoden
des DefaultHandlers für die einzelnen Ereignisse überschrieben werden, kann man die
Ereignisse individuell verarbeiten. Ein einfacher Handler, der lediglich die Ereignisse
auf die Kommandozeile ausgibt, ist in Quellcode 3.5 zu sehen.
3 eXtensible Markup Language
36
import org.xml.sax.helpers.DefaultHandler;
public class MySAXHandler extends DefaultHandler {
// DocumentHandler methods
/** Processing instruction. */
public void processingInstruction(String target,
String data) {
System.out.println("Processing Instruction:"
+"<?"+target+data+"?>");
}
/** Start document. */
public void startDocument() {
System.out.println("Start Dokument:");
}
/** Start element. */
public void startElement(String uri, String localName,
String qName, Attributes attrs) {
System.out.println("Start Element: <"+qName+">");
}
/** Characters. */
public void characters(char ch[],
int start,
int length) {
System.out.println("Characters: "
+new String(ch, start, length));
}
/** Ignorable whitespace. */
public void ignorableWhitespace(char ch[],
int start,
int length) {
characters(ch, start, length);
}
/** End element. */
public void endElement(String name) {
System.out.println("End Element: </"+name+">");
}
/** End document. */
public void endDocument() {
System.out.println("End of Document");
}
}
Quellcode 3.5: MySAXHandler.java: Handler zur Ausgabe der Ereignisse eines SAX-Parsers
3 eXtensible Markup Language
37
Dieser Handler wird dann folgendermaßen dem SAX-Parser mitgeteilt:
parser.setContentHandler(myHandler);
Die Erstellung eines Errorhandlers, das Einstellen der Features und das Initiieren des
Parsens sind identisch zu dem DOM-Parser und werden deshalb hier nicht nochmals
erläutert.
Vergleich: DOM vs. SAX
SAX ist sehr schnell und auch recht einfach, allerdings kann nach dem Parsen eines Ereignisses nicht mehr darauf zurückgegriffen werden, was bei einigen Anwendungen notwendig ist. Dies ist natürlich bei einem DOM möglich. Da der DOM-Tree im Arbeitsspeicher vorliegt, kann jederzeit auf beliebige Knoten zugegriffen werden. Der DOM-Parser
ist somit ideal für Anwendungen, die es erforderlich machen, auf das XML Dokument
interaktiv zuzugreifen. Allerdings ist das DOM auch wesentlich langsamer und speicherintensiver [Har2001].
Ein weiterer Unterschied liegt in der Validierung eines XML-Dokumentes. Sollten bei
der Validierung mit SAX Fehler im Dokument auffallen, kann es im schlimmsten Falle
sein, daß fast das gesamte Dokument verarbeitet wurde. Eventuell durchgeführte Aktionen müssen dann wieder rückgängig gemacht werden. Des weiteren kann bereits unnötig
viel Rechenzeit vergangen sein, da die einzelnen Ereignisse eine komplexe Verarbeitung
nach sich ziehen können. Beim DOM wird im Falle einer Validierung erst das gesamte
Dokument geprüft und es ist daher zu Beginn der eigentlichen Verarbeitung sicher fehlerfrei.
Bei der Wahl der Schnittstelle ist also zu prüfen, welche Anforderungen gestellt werden.
Wenn sehr komplexe Dokumente schnell geparst werden und diese Dokumente maschinell erstellt wurden, so daß sie wahrscheinlich fehlerfrei sind, ist ein SAX Parser auf
jeden Fall angebracht, bei umfangreichen Manipulationen am Dokument eher ein DOM
Parser.
3.4.2
Welche Parser gibt es?
Da XML-Anwendungen momentan sehr gefragt sind, gibt es auch eine Vielzahl von
XML-Parsern, die in der Regel gratis zur Verfügung gestellt werden. Sie unterscheiden
sich in einzelnen Features, der Performance oder in den Standards, die sie implementieren. Um einen kurzen Überblick über die momentan erhältlichen Parser zu geben,
sollen im folgenden die gängigsten XML-Parser und ihre Features vorgestellt werden
[Fine2001]:
Oracle Parser von der Oracle Corporation [Orac2001] Der Parser unterstützt sowohl
validierendes als auch nichtvalidierendes Parsen und entspricht der W3C XML 1.0
Recommendation19 . Der Parser bietet eine DOM Level 1 und eine SAX 1.0 API.
19
Festgeschriebener Standard des W3C
3 eXtensible Markup Language
38
Es können Dokumente in den Kodierungen20 UTF-8, UTF-16, ISO-10646-UCS-2,
ISO-10646-UCS-4, US-ASCII, ENDIC-CP-US, ISO-8859 und Shift SJIS geparst
werden.
Ælfred von Microstar [OpTe2001] Ælfred ist ein in Java entwickelter Parser und besticht durch eine sehr geringe Dateigröße von lediglich 26 kB und benötigt nur
sehr wenig Arbeitsspeicher. Er ist daher besonders für Java Applets geeignet. Allerdings bietet er lediglich einen SAX 1.0 Parser und keine DOM Implementation.
An Kodierungen werden UTF-8, UTF-16, ISO-10646-UCS-2, ISO-10646-UCS-4
und ISO-8859-1 unterstützt.
Xerces von Apache [Apac2001b] Der von Apache entwickelte Parser implementiert die
XML1.0 Recommendation und bietet eine DOM Level1 und 2 sowie eine SAX
1.0 und 2.0 API. Es werden folgende Kodierungen unterstützt: UTF-8, UTF-16,
ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, Chinese (big5), Japanese ISO2022-JP, Cyrillic (koi8-r), und viele weitere.
IBM’s XML for Java v2.0 von IBM [IBM2001] Der XML Parser von IBM basiert auf
dem XML Parser von Apache und wird von IBM lediglich unter einem anderen
Namen vertrieben. Die Features entsprechen also denen des Xerces Parsers.
Java API for XML Processing (JAXP) von Sun Microsystems [Sun2001b] JAXP unterstützt die DOM Level 1, DOM Level 2, SAX 1.0 und SAX 2.0 APIs. Zusätzlich
ist ein XSLT Prozessor integriert. Der JAXP Parser wird in die Java2 Platform
Standard Edition(J2SE) 1.4 mit eingebunden werden.
Es gibt noch viele weitere XML Parser und ständig kommen weitere auf den Markt. Einen
immer aktuell gehaltenen Überblick über die neuesten Parser findet sich unter [Fine2001]
und [Cove2001].
20
Die Kodierungen geben die Zeichensätze an, mit denen der Parser arbeiten kann
4 Das Java Native Interface
39
4 Das Java Native Interface
Wie bereits in Kapitel 2.4 erwähnt, existiert ein in C++ geschriebenes Software Development Kit, mit dem es möglich ist Flashfilme in C++ zu programmieren. Ziel der Arbeit ist
es, ein möglichst plattformunabhängiges Programm zu entwickeln. Würde man die Software in C++ entwickeln, wäre die Plattformunabhängigkeit nur noch bedingt gegeben. Es
wäre dann notwendig die Programme für jedes Betriebssystem einzeln zu kompilieren.
Wenn es dann noch gewünscht ist, Oberflächen zu implementieren, wäre die Plattformunabhängigkeit überhaupt nicht mehr gegeben.
Um dies zu umgehen, soll die Programmentwicklung in Java ermöglicht werden. Java ist,
wie in der Abbildung 4.1 zu sehen, eine platformunabhängige Programmiersprache, so
daß kein Neukompilieren der Quellen notwendig ist, wenn die Software unter verschiedenen Betriebssystemen eingesetzt werden soll.
HelloWorld.class
VM
VM
...
VM
Windows
Linux
...
OS / 2
Abbildung 4.1: Javaklassen können mit der Java Virtual Machine auf verschiedenen Betriebssystemen ausgeführt werden
Hierbei ergibt sich aber das Problem, wie dann Bibliotheken genutzt werden können,
die in einer anderen Programmiersprache als Java entwickelt wurden. Die Lösung lautet: Java Native Interface (JNI). JNI ist ein Teil des Java Development Kits (JDK) und
ermöglicht es Java Code, welcher auf der Java Virtual Machine (JVM) ausgeführt wird,
mit anderen Programmiersprachen wie C, C++ oder Assembler – sogenannte nativeSprachen – zu kommunizieren. Im allgemeinen wird JNI eingesetzt, wenn Programme
nicht ausschließlich in Java geschrieben werden können. So ist es beispielsweise in den
folgenden Situationen sinnvoll JNI zu nutzen:
• Die Standard Java Klassen bieten keine spezielle plattformabhängige Unterstützung,
welche für die Applikation notwendig ist.
• Es existiert bereits eine Bibliothek mit allen notwendigen Funktionen, welche Java
Applikationen zugänglich gemacht werden soll.
• Es ist zeitkritischer Programmcode notwendig, welcher zur Sicherheit in einer dem
Betriebssystem nahen Programmiersprache wie z.B. Assembler entwickelt werden
soll.
4 Das Java Native Interface
40
• Algorithmisch aufwendige Probleme mit hoher Laufzeit sollen möglichst effektiv
und schnell gelöst werden, was am besten in C oder Assembler möglich ist.
JNI stellt also eine Schnittstelle zwischen Java und einer anderen Programmiersprache
(z.B. C++) dar, wie in Abb. 4.2 zu sehen ist.
Java−Seite
Exceptions
C++ −Seite
J
Funktionen
Klassen
N
Bibliotheken
Java VM
I
Abbildung 4.2: JNI als Schnittstelle zwischen Java und C++
Es können Funktionen aus Klassen und Bibliotheken einer anderen Programmiersprache
direkt aus Javaklassen heraus aufgerufen werden, aber genauso ist der Zugriff von einer
beliebigen Programmiersprache auf Javaprogramme möglich. So kann z.B. aus einem
C++ Programm heraus die Java Virtual Machine gestartet, Exceptions erstellt oder Javamethoden aufgerufen werden.
Die Arbeitsweise von JNI und die Erstellung einer Java Applikation, welche auf eine
C++ Bibliothek zugreift, wird in den nächsten Abschnitten verdeutlicht.
4.1
Programmentwicklung mit JNI
Um native-Methoden in Java zu implementieren, ist ein mehrstufiger Prozeß nötig. Diese
einzelnen Stufen sollen an einem Beispiel dargestellt werden: Es wird ein HelloWorld
Programm entwickelt, welches es ermöglicht, von der Javaseite aus eine C++ Methode
aufzurufen, die einen Text und einige übergebene Parameter ausgibt.
Hierzu sind die folgenden sechs Schritte notwendig [Sun2001a] (siehe auch Abb. 4.3):
1. Zunächst wird das Javaprogramm geschrieben. Methoden, welche auf native-Code
zugreifen sollen, werden mit dem Schlüsselwort native deklariert (siehe auch
Quellcode 4.1 auf Seite 42)
2. Die Javaklasse wird mit javac Klassenname kompiliert.
4 Das Java Native Interface
41
3. Mit dem Kommando javah -jni wird ein Headerfile für die entsprechende
native-Methode erstellt. Dieses Headerfile ist die formale Struktur für die Methode
in der native-Sprache.
4. Nun wird die native-Methode in C oder C++ anhand der Vorgaben im Headerfile
implementiert (siehe auch Quellcode 4.3 auf Seite 43).
5. Die C oder C++ Quellen werden zu einer Shared Library kompiliert. Diese Library
muß auf dem System so abgelegt werden, daß sie auf dem LD LIBRARY PATH zu
finden ist.
6. Der letzte Schritt ist das Ausführen des Javaprogramms.
1.
Schreiben des
Javacodes
HelloWorld.java
2.
Mit javac
kompilieren
HelloWorld.class
3.
Headerfile erzeugen
javah −jni
HelloWorld.h
4.
Native Methode
implementieren
jni.h
stdio.h
HelloWorldImpl.cc
5.
Shared Library
erzeugen
libhello.so
6.
Javaprogramm
starten
Abbildung 4.3: Schritte der Programmentwicklung mit JNI
Die einzelnen Schritte werden im weiteren näher erläutert und mit Beispielcode versehen.
4 Das Java Native Interface
42
Schreiben des Javacodes
Der Quellcode 4.1 zeigt eine Javaklasse, die eine native-Methode definiert und zusätzlich
einen Staticblock besitzt.
public class HelloWorld {
public native void displayHelloWorld(int
iter,
String name);
static {
System.loadLibrary("hello");
}
public static void main(String args[]) {
new HelloWorld().displayHelloWorld(3, "Ralf Kunze");
}
}
Quellcode 4.1: HelloWorld.java: Klasse mit einer native-Methode
Jede Methode, die auf eine andere Programmiersprache zugreifen soll, wird mit dem
Schlüsselwort native versehen, wie es hier bei der Methode
public native displayHelloWorld(int iter, String name);
zu sehen ist. Diese Information wird später zur Erzeugung des zugehörigen Headerfiles
genutzt.
Der Staticblock in der Klasse HelloWorld dient dazu, die Shared Library zu laden,
welche z.B. in C oder C++ entwickelt wurde.
In der Main Methode kann dann die native-Methode ganz normal genutzt werden, wie es
der Programmierer von Java her gewohnt ist. Diese Klasse kann nun mit
javac HelloWorld
kompiliert werden.
Erzeugen des Headerfiles
Das zu der native-Methode gehörige Headerfile wird mit dem Kommando
javah -jni HelloWorld
aus der kompilierten HelloWorld.class Datei erzeugt, in diesem Fall also die Datei
HelloWorld.h (siehe Quellcode 4.2 auf Seite 43). In diesem Headerfile wird die Datei jni.h eingebunden, in der alle Definitionen und Deklarationen der Datenstrukturen
und Methoden angegeben sind, die von JNI zur Verfügung gestellt werden. Die Methodendeklarationen in dem erzeugten Headerfile besitzen eine Signatur, die dem Methodennamen auf der Java Seite entsprechen, aber noch einen zusätzlichen Präfix besitzen,
welcher sich aus dem Klassennamen – mit der Angabe des zugehörigen Packages – und
dem Wort Java, jeweils durch einen Unterstrich voneinander getrennt, zusammensetzt.
4 Das Java Native Interface
43
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
Method: displayHelloWorld
* Signature: (ILjava/lang/String;)
*/
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
(JNIEnv *, jobject, jint, jstring);
#ifdef __cplusplus
}
#endif
#endif
Quellcode 4.2: HelloWorld.h: Das mit javah -jni erzeugte Headerfile
Implementation der native-Methode
Die native-Methode wird gemäß der Vorgabe im generierten Methodenkopf im Headerfile implementiert (Quellcode 4.3).
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>
/* native class HelloWorld.c */
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
(JNIEnv *env, jobject obj, jint iterations, jstring j_name) {
const char* c_name = env->GetStringUTFChars(j_name,0);
for(int i=0;i<iterations;i++) {
printf("%s says: Hello World!\n", c_name);
}
}
Quellcode 4.3: HelloWorld.c: Die Implementation auf der C++ Seite
In diesem Beispiel wurde Wert darauf gelegt, daß sowohl primitive als auch komplexe
Datentypen übergeben werden, um den Unterschied in der Nutzung zu verdeutlichen.
Wie zu sehen ist, kann der primitive Datentyp der Javaseite int , welcher in C++ dem
jint entspricht, einfach ohne Umkonvertierung genutzt werden.
4 Das Java Native Interface
44
Der Datentyp String auf der Java Seite entspricht dem Datentyp jstring. Letzterer
ist in C++, aufgrund der komplexen Struktur des Datentyps, nicht direkt zugänglich.
Die Umwandlung eines jstring erfolgt über Funktionen, die über einen JNIEnv-Zeiger
abrufbar sind. Der JNIEnv-Zeiger verweist auf eine Tabelle, die alle möglichen Funktionen, die im Zusammenhang mit JNI-Datentypen-Konversionen stehen, enthält.
Erstellen einer Shared Library
Damit nun die Implementation der native-Methode in C++ dem Javaprogramm zugänglich wird, muß aus der entsprechenden C++ Klasse eine Shared Library erzeugt werden.
Um dies zu erreichen, wird die C++ Quelle zunächst zu einem Objektfile kompiliert.
Unter Linux geschieht dies unter Verwendung des gcc mit dem Kommando:
gcc -c -fpic -I/usr/lib/jdk1.3/include
-I/usr/lib/jdk1.3/include/linux HelloWorld.cc
Mit der Optionsflagge -c wird das Linken verhindert, es entsteht lediglich ein Objektfile.
Die Flagge -fpic erzeugt einen speziell für eine Shared Library vorkompilierten Code.
Die Flagge -I gibt an, wo nach den entsprechenden Includedateien gesucht werden soll.
Aus dem so entstandenen Objektfile wird nun mit dem Kommando
gcc -shared -fpic -I/usr/lib/jdk1.3/include
-I/usr/lib/jdk1.3/include/linux -o libhello.so
HelloWorld.o
eine Shared Library mit dem Namen libhello.so (unter anderen Betriebssystemen ist der
Aufruf entsprechend anders). Hier gibt die Flagge -shared an, daß eine Shared Library
erstellt werden soll. Mit der Flagge -o kann dahinterstehend der neue Name der kompilierten Library angegeben werden. In der Javaklasse wird die entstandene Shared Library
durch den folgende Staticblock geladen:
static {
System.loadLibrary{"hello");
}
Dafür muß allerdings die Shared Library zu finden sein. Unter dem Betriebssystem Linux
gibt es zum einen Standardverzeichnisse, in denen die Libraries abgelegt werden können
und zum anderen kann eine Variable mit dem Namen LD_LIBRARY_PATH mit den Pfaden gesetzt werden, in denen nach Shared Libraries gesucht werden soll. Außerdem muß
zum Laden der Library auf der Javaseite der Dateiname ohne lib “ und ohne die En”
dung .so “ angegeben werden. Sollte einer der beiden Punkte nicht erfüllt sein, kann die
”
Library nicht gefunden werden und es wird die Exception UnsatisfiedLinkError
erzeugt.
4 Das Java Native Interface
45
Ausführen des Javaprogrammes
Wenn die Shared Library korrekt installiert worden ist, kann das Javaprogramm gestartet
werden. Dies geschieht, wie in Java üblich, mit dem Kommando:
java HelloWorld
und liefert für die hier angeführte Beispielklasse folgendes Ergebnis:
Ralf Kunze says: Hello World!
Ralf Kunze says: Hello World!
Ralf Kunze says: Hello World!
4.2
Datentypen in JNI
Wie bereits erwähnt können primitive Datentypen ohne weiteres zwischen Java und anderen Programmiersprachen hin und her geschoben werden. Die Tabelle 4.1 stellt diese
primitiven Datentypen in Java und der native-Sprache gegenüber [Sun2001a].
Java Typ
boolean
byte
char
short
int
long
float
double
void
Native Typ
jboolean
jbyte
jchar
jshort
jint
jlong
jfloat
jdouble
void
Size in bits
8, unsigned
8
16, unsigned
16
32
64
32
64
n/a
Tabelle 4.1: Primitive Datentypen in JNI
Komplexere Java Datentypen können über JNI als Referenz an eine native-Methode übergeben werden. Alle Referenzen auf ein Javaobjekt sind vom Typ jobject, von dem
einige Typen abgeleitet werden. Welche Typen es gibt, ist in Tabele 4.2 auf Seite 46 zu
sehen.
Diese Objekte sind auf der native-Seite aber nicht direkt zugänglich, sie müssen erst konvertiert werden. So muß in einer native-Methode ein jstring Objekt, das im Unicode
vorliegt, erst mit der Methode GetStringUTFChars in einen UTF-8 String umgewandelt werden:
JNIEXPORT void JNICALL Java_Test_printLine
(JNIEnv *env, jobject obj, jstring line) {
const char* c_line = env->GetStringUTFChars(line,0);
printf("%s", c_line);
}
4 Das Java Native Interface
46
jobject
jstring
jclass
jarray
jobjectArray
jintArray
jlongArray
jfloatArray
jdoubleArray
jbooleanArray
jbyteArray
jcharArray
jshortArray
jthrowable
- alle Java Objekte
- java.lang.String Objekte
- java.lang.class Objekte
- Basis aller Arrayobjekte
- Objekt Arrays
- Integer Arrays
- Long Arrays
- Float Arrays
- Double Arrays
- Boolean Arrays
- Byte Arrays
- Charakter Arrays
- Short Arrays
- java.lang.Throwable Exceptions
Tabelle 4.2: Komplexe Datentypen bei der Nutzung von JNI
Um die Methode GetStringUTFChars aufzurufen, wird auf ein Objekt vom Typ
JNIEnv zurückgegriffen. Dieses Objekt stellt eine Referenz auf ein Javaobjekt dar,
über welches diverse Methoden aufgerufen werden können, um die Javaobjekte von der
native-Seite aus zu manipulieren, oder aber um eine Exception auf der Javaseite hervorzurufen. Die Methoden zum Umgang mit String-Objekten sind in der Tabelle 4.3
enthalten.
NewString()
NewStringUTF()
GetStringLength()
GetStringUTFLength()
GetStringChars()
-
GetStringUTFChars()
-
ReleaseStringChars()
ReleaseStringUTFChars()
-
Neuer String aus einem Unicode-C-String
Neuer String aus einem UTF8-C-String
Liefert die Anzahl der Unicode-Zeichen
Liefert die Anzahl der UTF8-Zeichen
Liefert Zeiger auf dynamisch allokierten Speicher, gefüllt mit den Unicode-Zeichen des
Strings
Liefert Zeiger auf dynamisch allokierten Speicher, gefüllt mit den UTF8-Zeichen des Strings
Gibt Speicherplatz des Unicode-C-Strings frei
Gibt Speicherplatz des UTF8-C-Strings frei
Tabelle 4.3: Methoden zum Umgang mit String-Objekten
Weitere Informationen zu den möglichen Datentypen, Methoden und weitere Möglichkeiten von JNI sind unter [Sun2001a] zu finden.
Mit dem Java Native Interface ist es also möglich, sowohl primitive als auch komplexe
Datentypen von der Javaseite auf die native-Seite zu schicken. Was aber ist mit selbst
definierten Objekten, die den Vorgaben von JNI nicht entsprechen und wie können komplexe Objekte von der native-Seite verwaltet oder genutzt werden? Auf diese Problematik
wird in Kapitel 6.1.1 näher eingegangen und eine Lösung für das letztere Problem vorgestellt.
5 Clipping
47
5 Clipping
Ein Vorteil von Macromedia Flash ist die Zoombarkeit eines Flashfilmes. So kann in
einen Film ohne Qualitätsverlust hineingezoomt werden, was vor allem bei Wetterkarten
von Vorteil ist. Wenn sich ein Betrachter einer Wetterkarte für eine Region näher interessiert, kann er diesen Teil vergrößern. Dies macht aber nur Sinn, wenn dann auch mehr
Informationen sichtbar werden. So ist es z.B. vorstellbar, daß der Betrachter zunächst nur
die Landeshauptstädte der Bundesländer sehen kann und erst nach dem Vergrößern des
Filmes auch weitere Orte sichtbar werden. Dies ist prinzipiell möglich, aber die entsprechenden Flashdateien werden dann wesentlich größer, so daß die Übertragung über das
Internet nicht mehr sinnvoll wäre, selbst wenn man bedenkt, daß Flashfilme streambar
sind, also bereits abgespielt werden können, wenn sie noch nicht ganz aus dem Internet geladen wurden. Wenn ein Flashfilm zu groß ist, wird die Darstellung des Filmes
schleppend vonstatten gehen, da sehr viel Arbeitsspeicher benötigt wird und das komplette Herunterladen eines Filmes bei zu vielen Daten im Film zu lange dauern kann.
Um dieses Problem zu umgehen, soll ein Film in hoher Auflösung in mehrere kleine
Teile zerlegt werden. Es werden Ausschnitte definiert, die einen einzelnen Flashfilm repräsentieren. Alle Informationen, die in diesem Ausschnitt nicht sichtbar sind werden
weggelassen. Diesen Vorgang nennt man Clipping.
In diesem Kapitel sollen die Grundlagen und die möglichen Probleme beim Clipping
erläutert werden.
5.1
Grundlagen des Clipping
Das Ziel des Clippens ist es, unbenötigte Informationen wegzulassen. Dies kann verschiedene Gründe haben. So ist es denkbar, daß aus einer Grafik lediglich ein Teil von
Interesse ist, oder aber das für das Rendern einer Grafik es nicht notwendig ist, die Randbereiche zu berechnen, da sie später auf dem Bildschirm nicht sichtbar wären. Aber
welche Informationen sind nun wesentlich und welche nicht? Beim Clipping werden
Grenzen angegeben, in denen die Informationen erhalten bleiben sollen. Diese Grenzen
können z.B. durch ein Rechteck oder ein Polygon definiert werden. Alle Informationen,
die innerhalb des Polygons oder Rechtecks enthalten sind, bleiben erhalten und alle anderen werden entfernt. Da im weiteren Verlauf lediglich Rechtecke als Clippinggrenzen
genutzt werden, wird auf das Clipping mit Polygonen als Clippinggrenzen nicht näher
eingegangen, Informationen hierzu sind unter [FAQ2001] erhältlich.
Die Schwierigkeit beim Clipping besteht darin, zu ermitteln, welche Objekte unsichtbar, teilweise oder ganz sichtbar sind. Wenn in einer Grafik ein Rechteck eingezeichnet
wird, ist es für den Betrachter dieser Grafik kein Problem mit bloßem Auge zu erkennen welche Teile der Grafik innerhalb des Rechtecks liegen und welche außerhalb. Soll
dies durch einen Algorithmus gelöst werden, ist dies nicht mehr trivial. Hierzu muß algorithmisch geprüft werden, wo sich ein Objekt in Bezug zu diesem Rechteck (also den
Clippinggrenzen) befindet, welche Schnittpunkte gegebenenfalls existieren und wie diese ermittelten Punkte ein neues Objekt bilden. Im folgenden werden drei grundlegende
Arten des Clippings näher erläutert: Das Punkt-, Linien- und Polygonclipping.
5 Clipping
5.2
48
Clipping von Punkten
Das Clippen von Punkten ist die grundlegendste Clippingart. Hierbei muß lediglich überprüft werden, ob ein Punkt innerhalb oder außerhalb des Clippingrechtecks liegt. Im letzteren Fall wird er entfernt, ansonsten wird er beibehalten. Der Test, ob ein Punkt sichtbar
ist oder nicht, ist denkbar einfach: Für eine Folge von Punkten Pi wird ein Test bezüglich
der Fenstergrenzen xmin ≤ xi ≤ xmax und ymin ≤ yi ≤ ymax durchgeführt. Alle Punkte
die diese Bedingung nicht erfüllen, werden entfernt.
5.3
Clipping von Linien
Das Linienclipping ist etwas aufwendiger, als das Punktclipping. In der Abb 5.1 werden
verschiedene mögliche Lagen einer Linie gezeigt.
ymax
ymin
x min
x max
Abbildung 5.1: Mögliche Lagen einer Linie im Bezug zum Clippingrechteck [Fell1992]
Je nach Fall ist eine Linie komplett innerhalb eines Clippingrechtecks und bleibt deshalb erhalten, oder ist im Gegensatz dazu völlig außerhalb der Clippinggrenzen und muß
dementsprechend entfernt werden. Wenn ein Punkt der Linie außerhalb liegt und der andere innerhalb des Clippingrechtecks, existiert ein Schnittpunkt, welcher errechnet werden muß, um die Endpunkte der neuen Linie zu erhalten. Wenn sich beide Punkte der
Linie außerhalb des Clippingrechtecks befinden heißt dies aber noch nicht, daß die Linie
nicht sichtbar ist. Es kann durchaus sein, daß in diesem Fall zwei Schnittpunkte existieren, welche dann die neuen Endpunkte der Geraden darstellen.
Der am weitesten verbreiteste Algorithmus zum Linienclipping stammt von Cohen und
Sutherland [Fell1992]. Hierbei wird die Ebene entsprechend den Clipping-Grenzen in
neun Bereiche eingeteilt und mit einem Bereichscode versehen, wobei das mittlere Rechteck, mit dem Code 0000, dem sichtbaren Teil entspricht (siehe Abbildung 5.2). Durch
diese Bereichscodes kann einfach geprüft werden, ob eine Linie sichtbar ist oder nicht.
Ob die Linie P1 P2 sichtbar ist, kann mit nur einer and-Verknüpfung sehr leicht überprüft
werden. Im Fall Code(P1 ) and Code(P2 ) <> 0 ist die Linie unsichtbar und muß nicht
5 Clipping
49
1001
1000
1010
0001
0000
0010
0101
0100
0110
Abbildung 5.2: Bereichscodes des Cohen and Sutherland Algorithmus [Fell1992]
geclippt werden. Ansonsten wird die Linie mit einer der Fenstergrenzen geschnitten, um
den Schnittpunkt zu erhalten. Anschließend wird der Test erneut durchgeführt. Wenn die
obige Bedingung erfüllt ist oder die Punkte beide innerhalb des Clippingfensters liegen,
wird die Iteration abgebrochen.
Das Linienclipping ist also noch recht einfach durchzuführen. Wie sieht es aber mit Linienzügen oder gar Polygonen aus?
5.4
Clipping von Polygonen
Das Clippen von Linien legt den Schluß nah, daß das Polygonclipping auf das sequenzielle Clippen der einzelnen Polygonlinien zurückzuführen ist, dem ist aber nicht so.
(a) vorher
(b) falsch
(c) falsch
(d) korrekt
Abbildung 5.3: Polygonclipping
Das Polygon würde in mehrere Teile zerfallen, wie in Abbildung 5.3b zu sehen ist. Um
dennoch ein vollständiges Polygon zu erhalten, müssen die Ein- und Austrittspunkte miteinander verbunden werden, wobei dies auch zu Problemen führen kann, wie in der Abbildung 5.3c zu sehen ist.
Um dennoch derartige Objekte clippen zu können, wurde von Sutherland und Hodgman
eine Methode entwickelt, mit der ein Polygon sequenziell an den Kanten des Clippingrechtecks abgeschnitten wird. Diese Methode wird Reentrant Polygon Clipping genannt
[Fell1992]. Das Polygon wird Clippingkante für Clippingkante geclippt. Punkte, die auf
der nicht sichtbaren Seite liegen, können weggelassen werden bzw. es wird ein Schnittpunkt errechnet. Die so erhaltenen Schnittpunkte und alle Punkte auf der sichtbaren Seite
5 Clipping
50
werden in ihrer Reihenfolge beibehalten und an der nächsten Kante wiederum diesem
Verfahren unterzogen. Der Vorgang ist in der Abbildung 5.4 zu sehen.
(a) oben
(b) rechts
(c) unten
(d) links
Abbildung 5.4: Polygonclipping nach Sutherland and Hodgman
51
II Realisierung und Anwendung
6 Realisierung
52
6 Realisierung
In den vorangegangenen Kapiteln wurden die Grundlagen vorgestellt, die für die weitere
Arbeit wichtig sind. Dieses Kapitel beschäftigt sich mit der praktischen Umsetzung der
Arbeit.
In den folgenden Abschnitten wird zunächst erläutert, wie eine Java API für das in C++
implementierte SWF-SDK von Macromedia entwickelt wurde. Darauf folgt dann die Erstellung des Dateiformates, welches dem Flash Generator, auf den im darauffolgenden
Abschnitt näher eingegangen wird, als Grundlage dient. In den folgenden Abschnitten
wird auf die Clippingalgorithmen eingegangen, die es erlauben eine SKF-Datei mit ihren
primitiven Objekten, Bezierkurven und Kontrolltags, die der Transformation von Objekten dienen, an waagerechten und senkrechten Linien zu clippen. Als letztes wird auf die
Performance des Generators eingegangen und gezeigt, wie der Generator und der Clipper
genutzt werden.
6.1
Erstellen einer Java API für das C++ SWF-SDK
In Kapitel 4.1 wird beschrieben, wie eine Anwendung entwickelt wird, die auf Funktionen und Methoden zurückgreift, die in einer anderen Programmiersprache entwickelt
wurden. Wie bereits erwähnt, ist es nur möglich, primitive Datentypen oder aber deren
Arrays als Parameter zu übergeben. Als komplexere Datentypen sind lediglich Strings
erlaubt. Wenn in den Methoden der C++ Library komplexe Objekte übergeben oder aber
C++ Objekte von der Methode zurückgeliefert werden, ist eine Anbindung an Java nur
schwer möglich. Es muß ein Weg gefunden werden, wie C++ Objekte die Grenze der JNI
Schnittstelle überwinden können.
Daher wurde das SDK zunächst dahingehend überprüft, ob eine Anbindung ohne weiteres möglich ist. Es wurde festgestellt, daß es unerläßlich ist Objekte als Parameter zu
übermitteln und sich die zurückgelieferten Objekte zu merken, damit sie bei späteren
Aufrufen wieder an eine C++ Methode übergeben werden können. Wie dies in C++ und
Java in Kombination mit JNI implementiert und wie die Java SWF API entwickelt wurde,
wird in den nächsten Abschnitten näher erläutert.
6.1.1
Verwaltung von C++ Objekten auf der Javaseite
Um C++ Objekte einfach auf der Javaseite zu verwalten, könnten alle entstehenden C++
Objekte in einer Hashtable auf der C++ Seite mit einer eindeutigen ID abgelegt werden.
Mit diesem Mechanismus wäre es lediglich notwendig, die eindeutige ID zwischen Java
und C++ hin und her zu schicken. Wenn also eine C++ Methode ein Objekt als Ergebnis
liefert (z.B. wurde ein Polygonobjekt erzeugt) wird das Objekt in eine Hashtable mit der
zugehörigen ID geschrieben. In der nativeMethode auf der Javaseite kommt lediglich
diese ID an, die natürlich in einem Datentyp abgelegt werden muß, der mit JNI ohne
weiteres übertragen werden kann. Soll nun dieses Objekt später weitergenutzt werden,
wird lediglich die entsprechende ID an die C++ Seite übermittelt. Daraus kann das Objekt
6 Realisierung
53
zurückverfolgt und mit ihm weitergearbeitet werden.
Es gibt aber noch eine einfachere Möglichkeit, welche grundsätzlich nach demselben
Prinzip arbeitet. Jedes Objekt in C++ besitzt einen Zeiger. Dies entspricht einer Adresse
im Speicher und ist eindeutig. Daher ist es wesentlich einfacher, diese Zeiger hin und her
zu schicken, um die Objekte zu identifizieren. Zusätzlich würde die Hashtable wegfallen
was aber einige Risiken mit sich bringt, die auf Eigenheiten der C++ Objekte beruhen.
Das Problem bei den C++ Objekten ist, daß ein Objekt durchaus verschiedene Zeiger
besitzen kann. Dieser Effekt tritt durch die in C++ mögliche Mehrfachvererbung auf. Wie
in der Abbildung 6.1 zu sehen ist, kann ein C++ Objekt, welches von einer Klasse stammt
die durch Mehrfachvererbung entstanden ist, über verschiedene Adressbereiche (Zeiger)
angesprochen werden, die für jede Klasse in der Vererbungshierarchie vorhanden sind.
Klasse A
Klasse B
Zeiger (A)c
Zeiger (B)c
Klasse C
B
Zeiger c
Objekt der Klasse C
A
C
Abbildung 6.1: Mögliche Zeiger eines C-Objektes bei Mehrfachvererbung
Wenn dies bei dem SDK der Fall ist, kann die Java Anbindung nicht wie beschrieben
implementiert werden, da ein und dasselbe Objekt verschiedene Referenzen hätte. Die
Analyse des SDKs hat ergeben, daß dies nicht der Fall ist und diese Vorgehensweise
daher durchführbar ist.
Nun stellt sich aber die Frage, wie die Objektzeiger so umgewandelt werden können,
daß sie mittels JNI auf die Javaseite gelangen können. Da ein Zeiger auf ein C++ Objekt
lediglich eine Adresse im Speicher darstellt, die in einem Bytecode abgelegt ist, kann
dieser Bytecode in ein Bytearray umgewandelt werden, welches ohne Probleme auf die
Javaseite gelangen kann. Im Beispielcode 6.1 wird diese Umwandlung durchgeführt.
jbyteArray ptr_to_byteArray (JNIEnv* env, void* p) {
jbyteArray arr = env->NewByteArray(sizeof(void*));
env->SetByteArrayRegion(arr,0,sizeof(void*),(jbyte*)&p);
return arr;
}
Quellcode 6.1: Methode zur Umwandlung eines Pointers in ein Bytearray
6 Realisierung
54
Zunächst wird ein jbyteArray mit der Größe eines Zeigers angelegt. Im nächsten
Schritt wird dann dieses Array über die Methode SetByteArrayRegion mit dem
entsprechenden Zeiger gefüllt. Dieses so entstandene jbyteArray wird dann an die
aufrufende Methode zurückgeliefert.
Falls nun aus Java heraus an ein C++ Objekt eine Methode geschickt werden soll oder
es nötig ist an eine C++ Methode ein C++ Objekt als Parameter zu übergeben, wird das
entsprechende Bytearray mitgeschickt und auf der C++ Seite wieder umcodiert, so daß
das C++ Objekt gefunden (siehe Beispielcode 6.2) und mit ihm weitergearbeitet werden
kann.
void* byteArray_to_ptr (JNIEnv *env, jbyteArray arr) {
void* p = NULL;
if (arr != NULL) {
env->GetByteArrayRegion(arr,0,sizeof(void*),(jbyte*)&p);
}
return p;
}
Quellcode 6.2: Methode zur Umwandlung eines Bytearrays in den entsprechenden Pointer
Hierzu wird zunächst ein leerer Zeiger angelegt und dieser dann über die Methode GetByteArrayRegion auf das entsprechende C++ Objekt gesetzt. Der so entstandene
Zeiger wird dann von dieser Funktion zurückgeliefert. Hierbei ist zu beachten, daß die
aufrufende Methode keine Information darüber erhält, um was für ein Objekt es sich
handelt, weshalb ein Cast21 in der aufrufenden Methode erforderlich ist. Wie kann nun
gewährleistet werden, daß der Cast stets korrekt durchgeführt wird?
Jedes Java Objekt, das ein C++ Objekt kapselt, erhält bei der Instanziierung den C++ Zeiger mitgeliefert. Dieser Zeiger wird in einer final Variablen abgelegt und kann somit
nicht mehr geändert werden. Daher sind Manipulationen an den Zeigern ausgeschlossen
und der Cast kann ohne Probleme durchgeführt werden.
Damit dieses Vorgehen auch wirklich durchführbar ist, ist es unabdingbar, daß die C++
Objekte nach dem Verlassen einer C++ Methode nicht einfach gelöscht werden, sondern
weiterhin existieren, damit sie später wiedergefunden werden können. Um dies zu erreichen, ist es notwendig, alle C++ Objekte, die auf diese Art und Weise genutzt werden
sollen, explizit mit new zu erzeugen. Diese Objekte bleiben dann so lange erhalten, bis
sie mit dem Destructor wieder zerstört werden.
Auf diese Art und Weise ist es nun möglich C++ Objekte zu verwalten und der Anbindung des SWF-SDKs steht nichts mehr im Wege. Wie sehen aber nun die Javaklassen
aus, die die SWF API bilden und wie ist es möglich, daß der Nutzer nicht mit den nativeMethoden in Kontakt kommt? Wie können Manipulationen unterbunden werden? Dies
soll im nächsten Abschnitt näher erläutert werden.
21
Umwandlung eines Datentypen in einen anderen, z.B. eine Integer in Float umwandeln
6 Realisierung
6.1.2
55
Konkrete Umsetzung: Entwicklung der SWF API
Die konkrete Umsetzung der Erstellung der Java SWF API ist nach der Analyse des SDKs
durch die Punkte in Kapitel 4.1 vorgezeichnet. Damit aber die Handhabung der SWF API
für den Nutzer möglichst einfach und sicher ist, sind noch einige Punkte zu beachten.
Ein großes Problem könnte auftreten, wenn die Bytearrays, welche die C++ Objekte
repräsentieren und in Javaobjekten gekapselt werden sollen, auf der Javaseite manipuliert
werden könnten, da so Objekte nicht mehr auffindbar wären und Speicherlecks entstehen
könnten. Zusätzlich soll für den Nutzer nicht ersichtlich sein, daß er mit native-Methoden
arbeitet. Um dies zu erreichen, wurden alle native-Methoden als private deklariert.
Daher sind diese Methoden von außen her nicht sichtbar und für den Nutzer der SWF Java
API nicht aufrufbar. Jede Klasse der API enthält also einen private und einen public
Teil. Die private Methoden dienen der Kommunikation mit dem C++ SDK und die
public Methoden sind für den Nutzer zugänglich. Sie konvertieren gegebenenfalls die
übergebenen Parameter und reichen versteckt die Bytearrays, welche die C++ Objekte
repräsentieren, an die native-Methoden weiter.
Um eine Manipulation der Bytearrays in den Objekten zu vermeiden, wird das Bytearay
mit den Schlüsselworten protected final versehen. protected gewährleistet,
daß es nicht für andere Pakete sichtbar ist und durch das Schlüsselwort final, daß es
nicht verändert werden kann. Alle Methoden, die nach außen hin aufrufbar sind, besitzen
eine zugehörige native-Methode, welche nach außen nicht sichtbar ist.
Im Quellcode 6.3 auf Seite 56 wird die Verarbeitung der C++ Referenz auf der Java-Seite
deutlich. Es ist zu erkennen, daß die C++ Referenz an die einzelnen native-Methoden
geliefert wird und keinesfalls diese Referenz von außen sichtbar ist oder manipuliert
werden kann.
6 Realisierung
package f3sdk.highlevel;
/**
* API for the HFObject class from the Macromedia Flash SDK.
* The C++ object is wrapped in a byetearray. Baseclass for
*
HFButton
*
HFFont
*
HFShape
*
HFEditText
*
HFOval
*
HFCircle
*
HFPolygon
*
HFRectangle
*
HFBitmap
*
HFText
*
HFSound
* @author Ralf Kunze @version 05.08.2000
*/
public class HFObject {
/**
* Objektreference on the C++ side */
protected final byte [] reference;
/**
* Set the depth value for the current shape
* @param depth The depth value to be set */
private native void SetDepth(byte[] reference, int depth);
/**
* Get the depth of this shape
* @return Returns the depth of this shape */
private native int GetDepth(byte [] reference);
//----- PUBLIC AREA -----------------/**
* Set the depth value for the current shape
* @param depth The depth value to be set */
public void setDepth(int depth) {
SetDepth(reference,depth);
}
/**
* Get the depth value of this shape
* @return Returns the depth of this shape */
public int getDepth() {
return GetDepth(reference);
}
}
Quellcode 6.3: HFObject Klasse, welche HFObject.cc für Java zugänglich macht
56
6 Realisierung
57
Wenn auf der Javaseite alle Klassen erstellt und die Headerfiles für die C++ Klassen erzeugt wurden, müssen diese noch in C++ implementiert werden. Hierbei werden einfach
in jeder zu implementierenden Methode die Aufrufe an das Macromedia SWF-SDK weitergereicht. Dieser Vorgang ist in der Klasse f3sdk highlevel HFObject.cc im
Quellcode 6.4 zu sehen.
#include
#include
#include
#include
<jni.h>
<f3sdk_highlevel_HFObject.h>
<HF3SDK.h>
<sdktools.h>
/*
* Class:
HFObject
* Method:
GetDepth
* Signature: ([B)I
*/
JNIEXPORT jint JNICALL Java_f3sdk_highlevel_HFObject_GetDepth
(JNIEnv *env, jobject obj, jbyteArray reference) {
HFObject *HFObj = (HFObject *)byteArray_to_ptr(env,reference);
return HFObj->GetDepth();
}
/*
* Class:
HFObject
* Method:
SetDepth
* Signature: ([BI)V
*/
JNIEXPORT void JNICALL Java_f3sdk_highlevel_HFObject_SetDepth
(JNIEnv *env, jobject obj, jbyteArray reference, jint depth) {
HFObject *HFObj =(HFObject *)byteArray_to_ptr(env, reference);
HFObj->SetDepth(depth);
}
Quellcode 6.4: Die zu HFObject.java gehörige native-Klasse f3sdk highlevel HFObject.cc
In der Methode GetDepth wird das C++ Objekt aus dem von der Java-Methode übergebenen Bytecode wiederhergestellt. Dieser Vorgang wurde in Kapitel 6.1.1 näher vorgestellt.
Anschließend wird dieses Objekt wie ein normales SWF-SDK Objekt verwendet. So
wird für jede Java-Klasse die korrespondierende C++ Klasse implementiert, die die Methodenaufrufe an das SDK weiterreicht.
Die so entstandenen C++ Klassen werden, wie in Kapitel 4.1 beschrieben, in einer Shared Library zusammengefaßt, wobei das SWF-SDK mit in diese Bibliothek eingebunden
wird. Auf diese Bibliothek können dann die Javaklassen verweisen und die implementierten Methoden nutzen.
6 Realisierung
6.1.3
58
Beseitigung von Fehlern in der Klasse HFPolygon
In der Klasse HFPolygon des SWF-SDK sind einige Methoden fehlerhaft. Da eine Manipulation des SDKs aus lizenzrechtlichen Gründen nicht in Frage kommt, müssen diese
Fehler bereits im Vorfeld, also auf der Javaseite vermieden werden. Außerdem sind einige Parameter nicht in eingängigen Datentypen abgelegt oder es dürfen keine absoluten
Koordinaten übergeben werden, sondern immer nur Änderungen zu dem vorherigen Status. Um dies für den Nutzer der SWF Java API zu vereinfachen, wurden die Parameter
entsprechend angepaßt.
Im C++ SDK ist vorgesehen, ein Polygon mit einem Punkt als Startpunkt anzulegen.
Weitere Liniensegmente (Geraden oder quadratische Bezierkurven) werden dann über
die Methoden AddStraightLine und AddCurvedLine angehängt. Diese beiden
Methoden erwarten als Parameter nicht die absoluten Koordinaten der Punkte, sondern
immer nur die relativen Änderungen zum zuletzt eingefügten Punkt. Dies wurde auch
in der Java SWF API so gehandhabt. Zusätzlich zu diesen beiden Methoden wurde es
aber auch ermöglicht reelle Koordinaten anzugeben, indem zwei Methoden implementiert wurden, die reelle Koordinaten in relative umwandeln und dann erst an die C++ Seite
weiterreichen. Diese Methoden sind im Quellcode 6.5 auf Seite 59 zu sehen. Außerdem
ist zu erkennen, daß die Koordinaten des neuen Punktes nicht in jedem Fall an das C++
SDK weitergereicht werden. Wenn ein Punkt doppelt eingefügt wird, also die relative
Änderung zum vorangegangenen Punkt gleich null ist, führt dies unweigerlich zu einem
Absturz des Flash Players. Um dies zu vermeiden, wird bereits im Vorfeld überprüft, ob
der Punkt eingefügt werden darf oder nicht.
6 Realisierung
59
/**
* Add a curved line to the polygon.
* All parameters are in twips.
*
* @param controlDX The x coord of the control point.
* @param controlDY The y coord of the control point.
* @param anchorDX The x coord of the anchor point.
* @param anchorDY The y coord of the anchor point.
*/
public void addCurvedLineCoords(int controlX, int controlY,
int anchorX, int anchorY)
{
// calculate the delta control and delta anchor values
// delta control values = controlcoords - oldanchorcoords
// delta anchor values = anchorcoords - controlcoords
int controlDX, controlDY, anchorDX, anchorDY;
controlDX = controlX - oldX;
controlDY = controlY - oldY;
anchorDX = anchorX - controlX;
anchorDY = anchorY - controlY;
//If controlDX and controlDY and
/ anchorDX and anchorDY is equal null
//the point is not allowed to be added, because
//it will lead into a crash of the flash player
if (controlDX!=0 || controlDY!=0 ||
anchorDX!=0 || anchorDY =!0) {
AddCurvedLine(reference, controlDX, controlDY,
anchorDX , anchorDY);
oldX = anchorX; oldY = anchorY;
}
}
/**
* Add a straight line to the Polygon.
* All parameters are in TWIPS
*
* @param x The x coordinate of the new vertex.
* @param y The y coordinate of the new vertex.
*/
public void addStraightLineCoords( int x, int y ) {
//If dx and dy is equal null
//the point is not allowed to be added, because
//it will lead into a crash of the flash player
if ((x-oldX)!=0 || (y-oldY)!=0) {
AddStraightLine(reference, x-oldX, y-oldY);
oldX=x; oldY=y;
}
}
Quellcode 6.5: Methoden zur Umwandlung absoluter in relative Koordinaten
6 Realisierung
6.2
60
Das Datenformat
Das Ziel dieser Arbeit ist vor allem die Visualisierung zeit- und raumbasierter Daten,
aber auch allgemeiner die Generierung von Flashfilmen aus einem einheitlichen Datenformat heraus. Für die zeit- und raumbezogenen Daten werden Wetterdaten genutzt, die
vom Deutschen Wetterdienst (DWD) geliefert wurden.
Das Lokal-Modell des DWD berechnet die Wettervorhersage der nächsten zwei Tage für
das Gebiet Mittel- und Westeuropa. Es enthält eine Prognose im Stundenabstand für ein
hochauflösendes Raster von nur sieben Kilometern Schrittweite. Diese Daten liegen im
Grib (Gridded Binary) Format vor. Das Gribformat ist ein universelles, bitorientiertes
Dateiformat für Rasterdaten, welches von Wetterdiensten zur Speicherung und zum Austausch ihrer Daten genutzt wird. Es wurde von der World Meteorological Organization
(WMO) als offener Standard definiert [WMO1998].
Eine GRIB-Datei kann aus einem oder mehreren Datensätzen (Records) bestehen, die
jeweils die Werte eines einzelnen Parameters für die Punkte eines rechteckigen Rasters
enthalten, z.B. die prognostizierten Werte für Temperatur, Niederschlag, Bewölkung oder
Luftdruck an einem bestimmten Ort zu einer bestimmten Zeit. Da diese Daten mit Macromedia Flash dargestellt werden sollen, müssen diese Rasterdaten in Vektordaten umgerechnet werden. Dies ist in [Stark2001] geschehen. Außerdem wurden sie in ein XML
basiertes Format geschrieben. Diese Daten könnten nun mit einem speziellen Programm,
einem Konverter oder auch Generator, direkt in einen Flashfilm umgewandelt werden.
Dies hätte aber den Nachteil, daß der Generator nur diese speziellen vektorisierten Wetterdaten umwandeln könnte.
Um auch andere Daten zu visualisieren, ist es notwendig ein einfach zu verarbeitendes
Schnittstellenformat zu schaffen, in welches die Vektordaten konvertiert werden können
(siehe Abb. 6.2).
Proprietäres
Grafikformat
Konvertierungs−
prozeß
Schnittstellen−
format
SKF 2 SWF
Flashfilm
Generator
Abbildung 6.2: Datenfluß bei der Generierung eines Flashfilmes
Ebenso wichtig ist die Formatsicherheit, d.h. das Dateiformat sollte von vornherein die
Möglichkeit bieten, Fehler in der Dateistruktur leicht zu erkennen.
Hier bietet sich eine XML-Anwendung besonders an. Wie in Kapitel 3 beschrieben, sind
XML-Dateien einfach zu verarbeiten, da bereits Lese- und Schreibmodule existieren und
die Dateien sowohl im Textformat vorliegen, als auch festen Regeln unterliegen, die garantieren, daß von dem einmal definierten Format nicht abgewichen werden kann.
In den folgenden Abschnitten werden das Schnittstellenformat und die Konvertierung der
Vektordaten in dieses Format erläutert.
6 Realisierung
6.2.1
61
Entwicklung der SKF-DTD
SKF steht für Stark Kunze Flash “ als Anlehnung an das SWF-Format, der Kombinati”
on der Daten von Benjamin Stark und dem in dieser Arbeit entwickelten Generator. Das
SKF-Format ist eine XML-Anwendung und, wie bereits in Kapitel 3.3 erwähnt, wird eine XML-Anwendung über eine DTD definiert. Die Erstellung einer DTD setzt voraus,
daß die zu strukturierenden Daten analysiert und wesentliche Elemente und Attribute in
ihrer hierarchischen Ordnung erfasst werden. Daher stellt sich als erstes die Frage, welche Daten beschrieben werden sollen.
In Flash gibt es verschiedene Objekte, so z.B. ein Polygon oder einen Kreis. Diese Objekte können mit verschiedenen Zusatzinformationen versehen werden, wie die Füllfarbe oder die Liniendicke. Diese einzelnen Objekte und deren Attribute könnte man als
Grundlage für eine mögliche DTD des Schnittstellenformates heranziehen.
Eine andere Art der Struktuierung der Daten wäre, die Struktur des Flashformates in einer DTD umzusetzen. Das SWF Dateiformat ist selbst tagbasiert und eben diese Tags
könnten dann in XML abgebildet werden. Diese beiden möglichen DTDs werden aber
noch durch eine dritte Möglichkeit ergänzt.
Das Macromedia Flash SDK ist in einzelne Klassen hierarchisch gegliedert, was ebenso in einer DTD abgebildet werden kann. Der Vorteil gegenüber den anderen beiden
Möglichkeiten ist die durch die Vererbung der C++ Klassen im SDK bereits vorhandene
hierarchische Gliederung. Eine derartige Gliederung hat zudem den Vorteil, daß der zu
entwickelnde Generator das SDK ebenfalls als Grundlage nutzt. Wenn also die Objekte
in der XML-Anwendung die Klassen- und somit die Objektstruktur des SDKs wiedergeben, ist der Generator wesentlich leichter zu implementieren. Also ist es zuerst nötig, daß
SDK in einzelne Elemente und Attribute einer DTD zu zerlegen.
Bei der Betrachtung der SWF-SDK Klassenhierarchie (siehe [Macr2001]) fällt auf, daß
es zunächst als wesentliches Element den Film gibt, der im SDK durch die HFMovieKlasse repräsentiert wird. Die zu einem Film gehörigen Attribute sind die Abspielrate
und die Größe des Filmes. Da der Name des Wurzelelementes der XML-Anwendung
den Inhalt repräsentieren sollte, wird es als skf benannt und erhält die Attribute des
Flashfilmes.
In der nächsten Hierarchiestufe ist dann ein Frame zu finden. Ein Frame ist ein kurzer Ausschnitt eines gesamten Flashfilmes, sozusagen ein einziges Bild eines gesamten
Filmes, auch Schlüsselbild genannt. Zwischen den einzelnen Frames finden keine weiteren Veränderungen statt. Die Objekte bleiben in ihrer Anordnung erhalten, bis in einem
folgenden Frame eine Transformation vorgenommen wird. Dabei können die einzelnen
Frames aber durchaus zeitlich weit versetzt sein. So ist es möglich, daß ein Frame zu
Beginn definiert wird und einige Objekte in diesem Frame platziert werden. Der nächste
explizit angegebene Frame in dem gegebenenfalls Änderungen an den Objekten vorgenommen wurden, liegt zeitlich gesehen wesentlich später. Beim Abspielen des Filmes
wird nun das erste Frame sichtbar und bleibt es solange, bis das nächste definierte Frame
erreicht wird.
Ein Frame wird im SDK durch die Klasse HFFrame repräsentiert. Das einzig notwendige Attribut eines Frames ist die Framenummer. Mit diesen beiden Elementen sieht die
DTD wie folgt aus:
6 Realisierung
62
<!ELEMENT skf ( (frame)* )>
<!ATTLIST skf rate
#CDATA "12"
width #CDATA "800"
height #CDATA "600" >
<!ELEMENT frame EMPTY>
<!ATTLIST frame number #CDATA "15">
Das frame-Tag enthält bisher noch keine weiteren Elemente. Welche weiteren Tags
gehören in das frame-Element?
Dies können Actions sein, die z.B. einen Film starten, anhalten oder aber zu einer bestimmten Stelle im Film springen. Die Klassen stammen alle von HFAction ab und
lauten: HFActionGoToURL, HFActionStop, HFActionPlay und HFActionGoToFrame. Die Klasse HFAction wird nicht in die DTD aufgenommen, da sie keine
Attribute enthält und bei der Nutzung des SWF-SDKs nie explizit aufgerufen wird. Daher werden nur die konkreten Actions – also die Subklassen von HFAction – in die
DTD mit ihren entsprechenden Attributen übernommen.
<!ELEMENT goToFrame EMPTY>
<!ATTLIST goToFrame frame CDATA #REQUIRED>
<!ELEMENT play EMPTY>
<!ELEMENT stop EMPTY>
<!ELEMENT getURL EMPTY>
<!ATTLIST getURL url
CDATA
target CDATA
#REQUIRED
#IMPLIED>
Im obigen Beispiel werden die Schlüsselwörter REQUIRED und IMPLIED genutzt, um
festzulegen, daß zum einen beim goToFrame-Tag eine Framenummer angegeben werden muß und zum anderen daß die Adresse, zu der beim getURL-Tag gesprungen werden soll, zwingend erforderlich ist. Die Festlegung in welchem Browserfenster diese Seite geöffnet werden soll ist jedoch nicht erforderlich.
Des weiteren sind grafische Objekte vorhanden, die alle von der Klasse HFShape abstammen. Die Klasse HFShape bietet Methoden, die für alle grafischen Objekte gültig
sind, so z.B. das Setzen der Liniendicke und -farbe der Umrandung der einzelnen Objekte oder die Füllfarben. Anstatt nun für jedes einzelne grafische Objekt ein Attribut oder
Element einzubinden, welches die Liniendicke festlegt, ist es sinnvoller ein Tag shape
zu definieren, welches diese Eigenschaften als Attributwert enthält. Erst dann kommt es
zu den speziellen grafischen Objekten mit ihren Eigenarten. Die elementaren grafischen
Objekte im SDK werden jeweils als Tag mit den zugehörigen Attributen abgebildet, so
erhält z.B. das Tag circle, welches im SDK die Klasse HFCircle repräsentiert, den
Radius und die Koordinaten des Mittelpunktes als Attribute.
6 Realisierung
63
<!ELEMENT swf ( (frame)* )>
<!ATTLIST swf rate
#CDATA "12"
width #CDATA "800"
height #CDATA "600" >
<!ELEMENT frame ( (shape)* )>
<!ATTLIST frame number #CDATA #REQUIRED>
<!ELEMENT shape (circle) >
<!ATTLIST shape lineWidth #CDATA #IMPLIED>
<!ELEMENT circle EMPTY>
<!ATTLIST circle centerX #CDATA #REQUIRED
centerY #CDATA #REQUIRED
radius #CDATA #REQUIRED>
Das Rechteck und das Oval werden mit ihren speziellen Attributen ebenso wie der Kreis
in die DTD aufgenommen. Als grafisches Objekt fehlt noch das Polygon, welches im
SDK durch die Klasse HFPolygon repräsentiert wird. Auch dieses Objekt wird mit
seinen speziellen Attributen unterhalb des shape-Tags eingeordnet. Das polygon-Tag
enthält aber im Gegensatz zu den anderen grafischen Objekten weitere Elemente. Dies
liegt daran, daß ein Polygon aus einer variablen Anzahl von Liniensegmenten besteht.
Diese Tatsache kann nicht mit einem einzigen Element modelliert werden. Es gibt zwei
verschiedene Liniensegmente: Zum einen ein gerades Segment und zum anderen eine
quadratische Bezierkurve, die jeweils als eigenständiges Element in der DTD festgeschrieben werden.
<!ELEMENT polygon ( (addStraightLine|addCurvedLine)*)>
<!ATTLIST polygon originX #CDATA #REQUIRED
originY #CDATA #REQUIRED>
<!ELEMENT addStraightLine EMPTY>
<!ATTLIST addStraightLine x #CDATA #REQUIRED
y #CDATA #REQUIRED>
<!ELEMENT addCurvedLine EMPTY>
<!ATTLIST addCurvedLine controlX
controlY
anchorX
anchorY
#CDATA
#CDATA
#CDATA
#CDATA
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
Das Tag addStraightLine fügt ein gerades Liniensegment mit dem Endpunkt x, y
zu dem Polygon hinzu, addCurvedLine fügt eine quadratische Bezierkurve mit einem
Kontroll- und einem Ankerpunkt an.
Diese Objekte sollen auch mit Füllfarben versehen werden, wofür die Tags mit verschiedenen Füllstilen in die DTD aufgenommen werden, die in einem Entity zusammengefaßt
sind.
6 Realisierung
64
<!ENTITY % FillStyles "( setFillColor |
setFillColorRGBA|
setLinearFillColors |
setLinearFillColorsRGBA |
setRadialFillColors |
setRadialFillColorsRGBA)">
Ein Entity ist ein Platzhalter, der nach seiner Definition genutzt werden kann, um die
DTD etwas übersichtlicher zu gestalten. Wenn ein polygon-Tag diese Füllarten beinhalten soll, müssen sie nun nicht mehr alle explizit angegeben werden, es reicht aus, das
Entity anzuführen.
<!ELEMENT rectangle %FillStyles;>
<!ATTLIST rectangle xmin %Integer;
ymin %Integer;
xmax %Integer;
ymax %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
Im obigen Beispiel fällt auf, daß noch ein Entity genutzt wird, das Entity Integer.
In der XML-Syntax ist es aber nicht möglich einen anderen Datentyp als CDATA oder
PCDATA für ein Attribut vorzuschreiben und dementsprechend kann von einem XMLParser diesbezüglich auch keine Überprüfung stattfinden.
Damit dem Benutzer der DTD aber klar ist, welcher Datentyp bei einem Attribut erwartet
wird, ist der Platzhalter Integer definiert worden, der selber nur auf den Datentyp
CDATA verweist.
<!ENTITY % Integer
"CDATA">
In Macromedia Flash gibt es drei verschiedene Arten, ein Objekt mit einer Farbe zu
füllen. Ein Objekt kann durchgehend mit einem definierten Farbton gefüllt werden, mit
einem linearen Farbverlauf von links nach rechts über zwei definierte Farben oder mit
einem radialen Verlauf, wobei eine Farbe den inneren Farbton angibt und die andere
den äußeren. Über die Angabe von Punktkoordinaten kann beim linearen Farbverlauf die
Stelle verschoben werden, an der sich die beiden Farben zu 50% vermischt haben und
beim radialen Farbverlauf kann das Zentrum, wo die innere Farbe zu 100% vorhanden
ist, verschoben werden. Die Farbwerte werden über die RGBA Werte (Rot, Grün, Blau,
Alphakanal) angegeben.
Um eine komfortablere Nutzung zu ermöglichen, ist ein Farb-Set mit vordefinierten
Farbwerten vorhanden, die über einen Namen angegeben werden können. Diese Namen
werden in dem Entity ColorName zusammengefaßt. Welche Farben auf diese Weise
definiert sind, ist dem Anhang A zu entnehmen.
Da es zwei verschiedene Wege gibt, eine Farbe zu definieren, gibt es auch pro Füllart
zwei verschiedene Elemente. Ein Element mit den entsprechenden RGBA Werten und
eines mit dem Farbnamen. Im folgenden werden die Tags setFillColorRGBA und
setFillColor, für das Füllen mit einer Farbe, und setLinearFillColorRGBA
und setLinearFillColor für die Füllung mit linearem Farbverlauf dargestellt. Eine
radiale Füllung ist mit dem Tag setRadialFillColor oder setRadialFillColorRGBA möglich, welches analog zu den Tags für die lineare Füllung aufgebaut ist.
6 Realisierung
65
<!ELEMENT setFillColorRGBA EMPTY>
<!ATTLIST setFillColorRGBA r %Integer;
g %Integer;
b %Integer;
a %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
<!ELEMENT setFillColor EMPTY>
<!ATTLIST setFillColor name %ColorName; #REQUIRED>
<!ELEMENT setLinearFillColorsRGBA EMPTY>
<!ATTLIST setLinearFillColorsRGBA
leftR
%Integer;
leftG
%Integer;
leftB
%Integer;
leftA
%Integer;
rightR %Integer;
rightG %Integer;
rightB %Integer;
rightA %Integer;
xCenter %Integer;
yCenter %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#IMPLIED
#IMPLIED >
<!ELEMENT setLinearFillColors EMPTY>
<!ATTLIST setLinearFillColors
leftColor %ColorName;
rightColor %ColorName;
xCenter
%Integer;
yCenter
%Integer;
#REQUIRED
#REQUIRED
#IMPLIED
#IMPLIED>
Eine weitere wesentliche Gattung von Klassen im SDK sind die Transformationen. Eine Transformation kann ein Objekt zu einem bestimmten Zeitpunkt verschieben, drehen
oder skalieren, wodurch ihre Attribute bereits festgelegt sind. So ist es bei einer Verschiebung notwendig anzugeben, wie weit in X- und Y-Richtung verschoben werden soll. Da
diese Transformationen zu einem bestimmten Zeitpunkt stattfinden sollen, werden sie
hierarchisch gesehen in der DTD direkt unter dem frame Tag eingeordnet, wodurch
der Zeitpunkt festgelegt wird. An dieser Stelle fällt aber ein Punkt auf, der bisher vernachlässigt wurde. Wie soll sich eine Transformation auf ein bestimmtes Objekt beziehen
können? Dies wird durch die Möglichkeit umgesetzt, in XML Referenzen zu vergeben.
Daher werden die grafischen Objekte um ein Attribut vom Typ ID erweitert.
<!ELEMENT rectangle %FillStyles;>
<!ATTLIST rectangle ID
ID
xmin %Integer;
ymin %Integer;
xmax %Integer;
ymax %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
6 Realisierung
66
Der Typ ID in XML garantiert, daß sie eindeutig ist, denn wenn dies nicht der Fall wäre,
käme es bei der Validierung einer solchen Datei mit einem XML-Parser zu einer Fehlermeldung. Dadurch wird sichergestellt, daß sich der Entwickler nicht um die Überprüfung
der IDs kümmern muß. Im Gegenzug erhalten alle Transformationstags einen Attributwert vom Typ IDREFS.
<!ELEMENT translate EMPTY>
<!ATTLIST translate USE IDREFS #REQUIRED
x
#CDATA #REQUIRED
y
#CDATA #REQUIRED>
Der Datentyp IDREFS bezieht sich immer auf eine vorher definierte ID. Wenn hier also
ein Wert angegeben wurde, der bis dahin in der XML-Datei nicht definiert wurde, tritt
beim Validieren der Datei eine Fehlermeldung auf. Desweiteren bietet der Typ IDREFS,
im Gegensatz zu dem Datentyp IDREF, die Möglichkeit, mehrere IDs anzugeben. Wenn
sich also eine Transformation auf mehrere Objekte beziehen soll, werden – wie im folgenden Beispiel – alle notwendigen IDs, durch ein Leerzeichen voneinander getrennt, als
Wert des Attributes angegeben:
<translate USE="ID1 ID2 ID3" x="100" y="50"/>
Als letztes sollen Textobjekte vorgestellt werden. Um mit dem SWF-SDK einen Text zu
erzeugen, muß zunächst ein Font über die Klasse HFFont definiert werden. Die hierfür
notwendigen Parameter sind der Name des Fonts und die Zeichen, die im Flashfilm verwendet werden sollen. Die Zeichen werden explizit angegeben, damit nur die Zeichen
enthalten sind, die auch wirklich genutzt werden und nicht alle im Font enthaltenen,
wodurch die Dateigröße minimiert wird. Im SKF-Format wird dies durch das Tag defineFont abgebildet.
<!ELEMENT defineFont (#PCDATA)>
<!ATTLIST defineFont fontname CDATA
bold
(true|false)
italic
(true|false)
ID
ID
#REQUIRED
"false"
"false"
#REQUIRED>
Dieses Tag erhält neben dem Namen des Fonts die Angabe, ob der Font fett oder kursiv
erscheinen soll. Innerhalb des Tags werden die Zeichen angegeben, die in den Flashfilm
übernommen werden sollen. Ein Textobjekt wird im SWF-SDK mit der Klasse HFText
erzeugt. In der SKF-DTD wird diese Klasse durch das Tag text repräsentiert.
<!ELEMENT text EMPTY>
<!ATTLIST text ID
usefont
data
height
xCoord
yCoord
ID
IDREF
CDATA
%Integer;
%Integer;
%Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#IMPLIED
#REQUIRED
#REQUIRED>
Da auch ein Textobjekt verschoben werden kann, wird ein Attribut ID definiert. Das
Attribut usefont gibt an, auf welches defineFont-Tag sich das Element bezieht. In
6 Realisierung
67
data ist der darzustellende Text enthalten, height gibt die Größe des Textes an und
als letztes werden die Koordinaten angegeben, wo der Text platziert werden soll.
In der SKF-DTD werden noch einige Tags mehr definiert. Diese sollen aber nicht näher
erläutert werden, da sie alle ähnlich wie die bisher angeführten Tags aufgebaut sind. Die
komplette SKF-DTD ist im Anhang A zu finden.
6.2.2
Konvertierung in das Schnittstellenformat
Das in dieser Arbeit entstandene Schnittstellenformat SKF ist für die Entwicklung des
Generators bestens geeignet. Wie sieht es aber nun aus mit den zu visualisierenden Daten? Generell sollen beliebige Daten visualisiert werden. In der vorliegenden Arbeit sollen exemplarisch zeit- und raumbezogene Daten am Beispiel von Wetterprognosen verwendet werden. Wie bereits erwähnt, stammen diese Wetterdaten vom Deutschen Wetterdienst und wurden von Benjamin Stark aus dem zur Verfügung gestellten Grib Rasterformat in vektorisierter Form in einer XML Datei abgelegt. Nun stellt sich die Frage, wie diese Daten in das Schnittstellenformat konvertiert werden können. Ein erster
Schritt ist die Konvertierung der Daten mittels der eXtensible Stylesheet Language Transformations (XSLT). In einer XSL-Transformation liest ein XSL-Prozessor sowohl ein
XML-Dokument, als auch ein XSL-Stylesheet ein. Basierend auf den Anweisungen im
XSL-Stylesheet gibt er ein neues XML-Dokument aus [Har2000]. Die Konvertierung der
Daten in eine dem Schnittstellenformat ähnliche Struktur – das SWFML-Format – wurde bereits in [Stark2001] durchgeführt. Allerdings ist das Format nicht vom Generator
zu verarbeiten, was eine weitere Transformation nötig macht. Bei dieser Transformation müssen die Namen der Tags und auch das Koordinatensystem angepaßt werden. Im
SWFML-Format sind alle Koordinaten in Pixeln angegeben. Das SKF-Format arbeitet
hingegen mit der Einheit TWIP, was einem zwanzigstel Pixel entspricht und eine wesentlich bessere Auflösung und Zoomfähigkeit bewirkt. Alle Koordinaten im SWFMLFormat müssen also mit 20 multipliziert werden. Diese Transformation ist aber nicht mit
XSLT durchführbar, da mit XSLT lediglich die Umbenennung und die Umstrukturierung
von Tags möglich ist und keine Berechnungen durchgeführt werden können. Daher muß
eine andere Möglichkeit gefunden werden, das SWFML-Format in SKF zu portieren,
was durch einen eigenen Filter, der die Daten einliest und in das SKF-Format umwandelt
erreicht wird.
Da das SWFML-Format maschinell erzeugt wurde und der Konvertierungsprozeß schnell
vonstatten gehen soll, basiert dieser Filter auf einem SAX-Parser. Die Daten werden vom
Parser eingelesen und die dabei entstehenden Ereignisse verarbeitet. Die Tagnamen werden angepaßt, und die Koordinatenwerte entsprechend umgerechnet.
Als erstes wird also ein SAX-Parser instanziiert, wie auf Seite 35 beschrieben, und diesem Parser ein Handler übergeben, der anhand der Ereignisse des SAX-Parsers aus einer
SWFML- eine SKF-Datei erzeugt. Dieser Handler verarbeitet dann die Ereignisse und
schreibt die konvertierten Daten in eine Datei. Um ein kurzes Beispiel für die Verarbeitung eines Tags zu geben, wird die startElement-Methode angeführt. Innerhalb
dieser Methode werden alle Tags in einem Block in der Methode individuell verarbeitet. Hier soll nur der Block für das Tag point vorgestellt werden. Die umfangreiche
Fehlerbehandlung wurde aus Gründen der Übersichtlichkeit weggelassen.
6 Realisierung
68
public void startElement(String uri,
String localName,
String qName,
Attributes attrs) {
if (qName.equals("point")) {
int x=0;
int y=0;
x = (new Integer(attrs.getValue("x"))).intValue();
y = (new Integer(attrs.getValue("y"))).intValue();
x=x*20;
y=y*20;
out.write("<addStraightLine x=\""+x+"\" y=\""+y+"\"/>");
out.newLine();
}
Da im SWFML-Format ein Punkt nur innerhalb eines polygon-Tags genutzt wird und
ein gerades Liniensegment definiert, muß dieses Tag in ein addStraightLine-Tag
umgewandelt werden. Hierzu werden zunächst die Attributwerte ermittelt und in TWIPS
umgerechnet. Anschließend wird das Tag über einen java.io.BufferedWriter
(hier die Variable out) in eine Datei geschrieben.
Alle anderen SWFML-Tags werden im Prinzip ebenso verarbeitet, allerdings sind einige
Tags nicht so einfach zu konvertieren wie das point-Tag.
Mit diesem Konverter ist es nun möglich, die Dateien aus [Stark2001] in das für den
Generator verständliche SKF-Format zu konvertieren.
6 Realisierung
6.3
69
Der Flash Generator
Der Generator ist das Herzstück der vorliegenden Arbeit. Er ist dafür zuständig, aus SKF
Daten einen Flashfilm zu generieren. Die XML basierten SKF Daten machen es erforderlich, daß der Generator dieses tagbasierte Format lesen kann, weshalb als Einlesemodul
ein XML Parser verwendet wird. Dieser erzeugt ein Document Objekt Model aus der
SKF Datei und reicht es dann zur Weiterverarbeitung an den Generator weiter. Obwohl
die Eingabedaten sehr umfangreich sind, wird ein DOM verwendet, da in einer SKF Datei
Referenzen auf bereits vorher definierte Objekte vorkommen können und diese in einem
DOM sofort anhand ihrer ID auffindbar sind. Würde ein SAX Parser eingesetzt, müßte
die Datei erneut eingelesen werden, um die referierten Elemente zu erhalten, was einen
Mehraufwand darstellen würde. Das zu einem DOM geparste Dokument wird dann vom
Generator Stück für Stück interpretiert und in einen Flashfilm umgewandelt.
Wie im Kapitel 3.4.2 gezeigt wurde, gibt es eine Vielzahl von verschiedenen XMLParsern und es stellt sich die Frage, welcher XML-Parser eingesetzt werden soll. An den
XML-Parser werden einige Grundvoraussetzungen gestellt. Der XML-Parser muß einen
DOM-Parser mitliefern, da dies für den Generator unerläßlich ist. Dieser DOM-Parser
muß die DOM Level 2 Spezifikationen beherrschen, da es erst ab DOM Level 2 möglich
ist, zu einer bestimmten Stelle im DOM-Tree zu springen, was beim Generator häufig
der Fall sein kann.
Diese Bedingungen erfüllen von den im Kapitel 3.4.2 vorgestellten Parsern nur der Xerces
XML-Parser von Apache, der IBM’s XML for Java v2.0 von IBM und der JAXP XMLParser von Sun Microsystems. Da die Parser von Apache und IBM identisch sind und
lediglich unter anderem Namen vertrieben werden, muß nur zwischen zwei Parsern unterschieden werden. Nach einem Performance Test unter [Scho2000] ist der Xerces DOMParser der schnellere Parser von beiden. Daher wird er auch im folgenden für den Generator eingesetzt.
Damit der Generierungsprozeß ohne Probleme vonstatten geht, muß bei der Implementation auf verschiedene Punkte geachtet werden:
• Der Generator soll möglichst flexibel sein. Wenn in der SKF Struktur Änderungen
vorgenommen werden, soll der Generator schnell anzupassen sein.
• Es sollen platzsparende Filme erzeugt werden.
• Der Generierungsprozeß muß zügig vonstatten gehen.
• Der Generator sollte stabil sein und nicht zu Programmabstürzen neigen.
Wie die genannten Punkte umgesetzt werden, wird in den nächsten Abschnitten erläutert.
6.3.1
Dynamik und Flexibilität des Generators
Der Generator ist von seiner Struktur her in viele Einzelelemente aufgeteilt, weshalb
weitere Elemente leicht eingefügt oder bestehende schnell geändert werden können. Jedes einzelne Element in der SKF-DTD besitzt eine entsprechende Klasse als Gegenstück
6 Realisierung
70
im Generator. Wenn sich also die Attribute eines Elementes ändern sollten, muß lediglich eine Klasse umgeschrieben werden und schon ist der Generator angepaßt. Ähnlich
verhält es sich, wenn ein Element in der SKF-DTD hinzukommen sollte. Dann kann
eine Klasse implementiert werden, die dieses Element interpretieren kann, welche zu
den Generatorklassen hinzugefügt wird. Allerdings stellt sich hierbei die Frage, wie
dann diese neu hinzukommende Klasse vom Generator erkannt wird, damit sie auch
entsprechend aufgerufen werden kann. Um dieses Problem zu lösen, werden die Klassen untereinander nicht explizit mit dem Klassennamen statisch aufgerufen, sondern dynamisch über das java.lang.reflect-Paket, je nachdem welches Tag im DOM
gefunden wird. Die dazugehörige Klasse wird in diesem Augenblick geladen und ihr
werden die notwendigen Informationen übergeben. Mit dieser Technik wurde gleich ein
zweites Problem gelöst, denn manche Elemente können verschiedene Unterelemente in
unterschiedlicher Häufigkeit besitzen und es wären aufwendige Stringvergleiche in ifAnweisungen nötig, um diese Elemente entsprechenden Klassen zuzuordnen. Mit dem
java.lang.reflect Paket ist dies nicht nötig. Wie aber werden nun dynamisch
Klassen geladen und zugehörige Objekte instanziiert?
In Quellcode 6.6 ist ein Beispiel angegeben, wie im Generator eine Klasse dynamisch
geladen und ein Objekt instanziiert wird.
//Call the parse method with reflect
Class clazz = null; Constructor c = null;
ParsingObjekt p = null;
//toParse is part of a DOM
//getNodeName() returns the name of the tag
//get the class
try { clazz = Class.forName("P"+toParse.getNodeName()); }
catch (ClassNotFoundException e) {
System.err.println("No class P"+toParse.getNodeName());
}
//get the constructor
try { c = clazz.getConstructor(new Class[] {}); }
catch (NoSuchMethodException e) {
System.err.println("No Constructor P"
+toParse.getNodeName());
}
//get an instance from the class
try { p = (ParsingObject)c.newInstance(new Object[]{}); }
catch(Exception e) {
System.err.println("!!! can’t instanciate object !!!");
}
p.parse(toParse,frame,om);
Quellcode 6.6: Dynamische Instanziierung eines Objektes
6 Realisierung
71
In diesem Beispiel enthält die Variable toParse einen Teil eines DOMs, also einen
Teilbaum, und zwar genau den Teil, der gerade vom Generator abgearbeitet werden soll.
Mit der Methode getNodeName() kann der Name der Wurzel des Teilbaumes ermittelt
werden, was einem Tag der SKF-DTD entspricht. Dieses Tag soll nun an die verarbeitende Klasse weitergeleitet werden.
Als erstes wird mit der Methode forName(String Klassenname) aus der Klasse
Class versucht, die Klasse zu laden, die dem Tagnamen entspricht. Es wird ein P vorangestellt, da alle Klassen, die ein Tag parsen können, mit einem P “ beginnen, gefolgt
”
von dem Tagnamen wie er in der SKF DTD definiert ist. Wenn diese Klasse auf dem
Klassenpfad nicht gefunden wird, kommt es zu einer ClassNotFoundException.
Im nächsten Schritt wird der Constructor der geladenen Klasse ermittelt und in der Variablen c abgelegt. Falls kein passender Constructor existiert, wird eine NoSuchMethodException geworfen. Wenn ein passender Constructor gefunden und das ConstructorObjekt initialisiert wurde, kann mit der Methode newInstance() ein Objekt der Klasse erzeugt werden. Dieses Objekt verhält sich nun genau wie ein auf herkömmlichem
Wege erzeugtes Objekt.
Diese Vorgehensweise wird immer dann angewendet, wenn im DOM weitere Elemente
geparst werden müssen. Durch diese Technik ist eine hohe Flexibilität und Anpaßbarkeit
des Generators gewährleistet.
Es wurde also gezeigt, wie die Tags in einer SKF-Datei dynamisch verarbeitet werden,
indem die zugehörigen Klassen über Ref lect aufgerufen werden. Was aber hat es mit den
zu einem Tag gehörigen Klassen auf sich und wie erhält man die zu einem Tag gehörigen
Informationen? Das soll im nächsten Abschnitt näher erläutert werden.
6.3.2
Implementation
Damit eine SKF-Datei verarbeitet werden kann, muß sie zunächst eingelesen werden.
Dies ist mit einem XML-Parser ohne Probleme möglich. Es gibt, wie im Kapitel 3.4.1 beschrieben, zwei verschiedene Arten, ein XML-Dokument einzulesen. Einmal über einen
DOM-Parser, der das Gesamte XML-Dokument im Speicher als Baum, dem sogenannten
DOM-Tree aufbaut. Die andere Möglichkeit ist, einen SAX-Parser zu benutzen, der beim
Parsen des Dokumentes einzelne Ereignisse erzeugt, die dann weiterverarbeitet werden
können.
Für den Generator soll ein DOM-Parser als Lesemodul eingesetzt werden, obwohl dieser wesentlich langsamer ist und mehr Arbeitsspeicher benötigt als der SAX-Parser. Dies
liegt daran, daß es beim Generieren eines Flashfilmes notwendig sein kann, auf früher
definierte Elemente zurückzugreifen, was mit einem SAX-Parser so nicht möglich wäre,
wie es beim Tag placeClonedObjekt erforderlich ist.
Das placeClonedObjekt-Tag enthält eine Referenz auf ein grafisches Objekt in der
SKF-Datei, welches genauso, wie es bereits definiert wurde, erneut im Film platziert
werden soll. Mit einem DOM-Tree kann direkt zu dem betreffenden Element gesprungen und die dortigen Informationen verarbeitet werden. Dies wäre mit dem SAX-Parser
nicht möglich. Das SKF-Dokument wird also mit einem DOM-Parser verarbeitet und der
daraus resultierende DOM-Tree wird innerhalb des Generators in einen Flashfilm umgewandelt.
6 Realisierung
72
Jedes einzelne in der SKF-DTD definierte Tag besitzt unterschiedliche Attribute, die individuell verarbeitet werden müssen. Daher existiert für jedes einzelne Tag eine entsprechende Klasse im Generator, welche speziell dieses Tag verarbeiten kann. Die Klassen
besitzen denselben Namen wie das Tag, es wird lediglich ein P “ vor dem Tagnamen
”
vorangestellt, da sie ja wie im vorangegangenen Abschnitt erläutert über Ref lect dynamisch geladen werden.
Jede Klasse implementiert das Interface flashweather.generator.ParsingObject. In dem Interface ist die folgende Methode definiert:
public Object parse(Object tree,
Object hfObject,
StoringManager sm);
Der erste Parameter enthält einen Teil des beim Parsen entstandenen DOM-Trees, der
zweite das hierarchisch übergeordnete Objekt und mit dem letzten Parameter wird ein
StoringManager übergeben, in dem alle erzeugten Objekte abgelegt werden. Die
Klasse StoringManager beinhaltet eine Hashtable, in der die erzeugten Objekte mit
ihrem zugeordneten Schlüsselwert – die in der SKF-Datei vergebene ID – gespeichert
werden. Die Objekte werden gespeichert, damit sie zu einem späteren Zeitpunkt, z.B.
durch Transformationen, manipuliert werden können und hierfür anhand ihrer ID leicht
wiedergefunden werden können.
Die Implementation der Methode parse ist für alle Tags recht ähnlich und verläuft
immer in drei Schritten. Als erstes werden die Attribute eines Elementes aus der Wurzel
des aktuellen Teilbaumes des DOM-Trees geholt, danach wird das Objekt über die Java
SWF API erzeugt und als letztes werden die Elemente unterhalb der Wurzel verarbeitet.
Die Attribute aus dem Wurzelelement (im Quellcode toParse genannt) des Teilbaumes
erhält man, wie im folgenden Ausschnitt der Ppolygon-Klasse:
NamedNodeMap attributes = toParse.getAttributes();
try {
originX = (new Integer(attributes.getNamedItem("originX").
getNodeValue())).intValue();
}
catch (NumberFormatException e) {
throw new GeneratorException("Wrong data in originX: "
+"integer value required!");
}
try {
originY = (new Integer(attributes.getNamedItem("originY").
getNodeValue())).intValue();
}
catch (NumberFormatException e) {
throw new GeneratorException("Wrong data in originY"
+"integer value required!");
}
6 Realisierung
73
Da für jedes SKF-Tag eine eigene Klasse existiert, ist darin bekannt, welche Attribute
in diesem Tag möglich sind, weswegen die Attribute ohne Probleme in der Klasse weiterverarbeitet werden können. Bei der Bestimmung der Attribute ist allerdings ein Punkt
zu beachten: Da in XML keine Typüberprüfung möglich ist, muß dies in der zum Tag
gehörigen Klasse geschehen. Falls wie im obigen Beispiel ein Integer Wert erwartet wird,
aber der Inhalt des Attributes diesem Typ nicht entspricht, ist eine Fehlerbehandlung notwendig. In einem derartigen Falle wird eine GeneratorException geworfen, die in
einem String die Fehlermeldung enthält.
Im nächsten Schritt wird das Objekt über die Java SWF API mit den Attributen erzeugt.
polygon = new HFPolygon(originX,originY);
Damit dieses Objekt zu einem späteren Zeitpunkt manipuliert werden kann, wird es in
einem StoringManager mit der zugehörigen ID des Objektes abgelegt.
sm.keep(attributes.getNamedItem("ID").getNodeValue(),
polygon);
Der letzte Schritt ist die Verarbeitung aller möglichen Unterelemente, so kann z.B. ein
polygon-Tag eine beliebige Anzahl aus Liniensegmenten besitzen. Hierzu müssen zuerst die Elemente unterhalb der Wurzel des Teilbaumes ermittelt werden, dies geschieht
mit:
NodeList nodes = toParse.getChildNodes();
Die so erzeugte NodeList kann mit einer for-Schleife abgearbeitet werden.
for (int k=0;k<nodes.getLength(); k++) {
Node childNode = nodes.item(k);
}
Innerhalb der Schleife wird dann die zum Child-Tag gehörige Klasse wie in Kapitel 6.3.1
beschrieben über Reflect geladen und ein Objekt erzeugt, an die die parse-Methode mit
dem Teilbaum des Unterelements als Parameter geschickt wird. Innerhalb dieser Methode wird dann der Generierungsprozeß eine Hierarchiestufe tiefer fortgesetzt.
Die Methoden im Zusammenhang sind im Quellcode 6.7 auf Seite 74 angeführt, inklusive der Verarbeitung der Elemente unterhalb des Wurzelelementes mit Reflect. Die umfangreiche Fehlerbehandlung wurde aus Gründen der Übersichtlichkeit stark vereinfacht.
Alle Klassen des Generators sind in dieser Form aufgebaut. Die Klassenstruktur ist im
Anhang C abgebildet.
Da bei der Erzeugung eines HFFont-Objektes aufwendige algorithmische Probleme zu
lösen sind, soll speziell die Generierung von Fonts im nächsten Abschnitt näher betrachtet werden.
6 Realisierung
74
public Object parse(Object tree,Object hfObj,StoringManager sm){
toParse = (Node)tree;
frame = (HFFrame)hfObj;
NamedNodeMap attributes = toParse.getAttributes();
try {
originX=(new Integer(attributes.getNamedItem("originX").
getNodeValue())).intValue();
originY=(new Integer(attributes.getNamedItem("originY").
getNodeValue())).intValue();
}
catch (Exception e)
throw new GeneratorExeption("Wrong value");
polygon = new HFPolygon(originX,originY);
sm.keep(attributes.getNamedItem("ID").getNodeValue(),
polygon);
NodeList nodes = toParse.getChildNodes();
for (int k=0;k<nodes.getLength(); k++) {
Node toParse = nodes.item(k);
Constructor c = null;
Class clazz = null;
ParsingObject p = null;
try{
clazz = Class.forName("P"+toParse.getNodeName());
c = clazz.getConstructor(new Class[] {});
p = (ParsingObject)c.newInstance(new Object[] {});
}
catch (Exception e)
throw new GeneratorException("Error in reflect");
p.parse(toParse,polygon, sm);
} //if
} //for
frame.addObject(polygon);
return polygon;
}
Quellcode 6.7: Methode parse aus der Klasse Ppolygon zur Verarbeitung des polygonTags
6 Realisierung
6.3.3
75
Einbinden von Fonts
Ein Grundsatz in Flash ist, daß eine SWF Datei von keinen außenstehenden Ressourcen
abhängig sein darf. Dementsprechend müssen auch die verwendeten Fonts in den Flashfilm eingebunden werden. Wenn nun in der SKF Datei ein Font mit Namen angegeben
wird, besteht für den Generator das Problem, genau diesen Font auch erzeugen zu müssen
und in den Film einzubauen. Wie erhält man aber die Definition, also die genauen Umrisse, eines Fonts?
Hierzu wird auf die systemweit installierten TrueType-Fonts zurückgegriffen. Dazu sind
einige Voraussetzungen erforderlich. Als erstes muß der Generator Zugriff auf diverse
Fonts erhalten. Dies wird in diesem Falle mit der Javaklasse java.awt.Font realisiert. Diese Klasse stellt verschiedene Methoden zur Verfügung, um Fonts zu laden und
zu manipulieren. Damit aber diese Klasse genutzt werden kann, muß es ihr ermöglicht
werden, auf den grafischen Kontext zuzugreifen, der nur dann existiert, wenn ein WindowServer gestartet ist. Der Generator ist also für den Fall, daß Fonts eingesetzt werden, also nur unter grafischen Benutzeroberflächen wie z.B. KDE, Windows, etc. einsetzbar.
TrueType-Fonts sind im allgemeinen systemweit installiert, wobei es keine Festlegung
gibt, welche Fonts vorhanden sein müssen und welche nicht. Dies ist völlig von der Installation des Systems abhängig. Wie ist aber nun ein TrueType-Font definiert und wie
kann er in einen Flashfilm eingebunden werden?
Ein Buchstabe eines TrueType-Fonts ist in einem EM-Quadrat enthalten. Dieses EMQuadrat ist so groß, daß jeder Buchstabe des Fonts hineinpaßt und ist nicht mit einer
BoundingBox eines Objektes zu verwechseln. Die gesamte Höhe eines Buchstabens wird
Body genannt, die Höhe des Zeichens über der Schriftlinie wird mit Ascent und unter der
Schriftlinie mit Descent bezeichnet. Zur besseren Verdeutlichung der Begriffe sind sie in
der Grafik 6.3 noch einmal veranschaulicht.
Q
Ascent
Body
EM
Descent
Abbildung 6.3: Ausmaße eines TrueTypeFont-Buchstaben
Wie erhält man nun die Hüllkurve eines TrueType-Fonts in Java?
Bei einem TrueTypeFont besteht die Hülle eines Buchstabens aus einem Linienzug aus
geraden und kurvigen Segmenten. Dieser Font kann gemäß den Vorgaben in der SKFDTD fett oder kursiv sein. Um diesen Linienzug zu erhalten, müssen diese Vorgaben aus
dem DOM-Tree Knoten defineFont ermittelt und ein java.awt.Font Objekt mit
den entsprechenden Attributen angelegt werden.
6 Realisierung
76
if ((attributes.getNamedItem("bold").
getNodeValue()).equals("true"))
style +=Font.BOLD;
if ((attributes.getNamedItem("italic").
getNodeValue()).equals("true"))
style +=Font.ITALIC;
java.awt.Font f = new Font(name, style, 1024);
Im obigen Quelltext werden als erstes die Attributwerte des defineFont-Tags ermittelt
und bitcodiert in einer Variablen abgelegt. Darauf wird dann ein Objekt der Klasse java.awt.Font mit dem Fontnamen, dem Stil und der Größe erzeugt.
Von diesem Font kann über ein java.awt.font.GlyphVector-Objekt die Hülle
ermittelt werden. Der Glyphvektor wird folgendermaßen ermittelt:
java.awt.font.GlyphVector glyphs =
f.createGlyphVector(new FontRenderContext(null,
true,
false),
chars );
Der Glyphvektor enthält die im Parameter chars angegebenen Zeichen. Die Hülle des
Fonts kann dann in einem java.awt.Shape-Objekt abgelegt und über ein Objekt der
Klasse java.awt.geom.PathIterator Punkt für Punkt durchlaufen werden.
java.awt.Shape s = glyphs.getGlyphOutline(number);
PathIterator path =
s.getPathIterator(new AffineTransform());
int [] coords;
while (!path.isDone() ) {
int pointType = path.currentSegment(coords);
path.next();
}
Die Methode currentSegment(int[] coords) liefert im Parameter coords
die Punktkoordinaten und als Rückgabewert die Art des Punktes. Es gibt im wesentlichen vier verschiedene Punkt-Arten:
MOVETO: Startpunkt für einen neuen Linienzug
LINETO: Punkt, zu dem eine gerade Linie gezogen wird
QUADTO: Zwei Punkte, die eine quadratische Bezierkurve definieren
CLOSE: Die Linie ist beendet
Auf diese Art und Weise sind nun alle Hüllpunkte des Fonts bekannt und können weiterverarbeitet werden.
6 Realisierung
77
Um mit dem SWF-SDK einen Font anzulegen, muß zunächst aus der Hüllkurve eines
Buchstabens ein HFPolygon-Objekt erzeugt werden. Dies stellt auf den ersten Blick
kein wesentliches Problem dar, denn die Liniensegmente des Fonts sind entweder quadratische Bezierkurven oder gerade Linienstücke, wie es auch bei einem HFPolygonObjekt der Fall ist. Allerdings sind die Koordinatensysteme bei einem HFFont-Objekt
nicht mit den TrueTypeFont-Koordinaten identisch. Dies liegt daran, daß ein Font in SWF
immer in einem EM-Quadrat der Größe 1024*1024 angelegt wird, aber TrueType-Fonts
ein beliebig großes EM-Quadrat besitzen. Um diese Umrechnung vorzunehmen, muß als
erstes die Größe des EM-Quadrats des TrueType-Fonts ermittelt werden.
Rectangle2D.Float boundingBox =
f.getMaxCharBounds(new FontRenderContext(null,
true,
false));
double em_height = boundingBox.getHeight();
Nun wird jeder Punkt des TrueTypeFonts mit dem Wert em height/1024 auf das
SWF EM-Quadrat konvertiert. Aus diesen Punkten werden dann HFPolygon-Objekte
erzeugt, die in einem HFFont-Objekt abgelegt werden. Hierbei gibt es aber ein letztes
Problem: Der Abstand zwischen den einzelnen Zeichen in einem TrueType-Font ist immer unterschiedlich, dieser Abstand wird Advance genannt. In der Grafik 6.4 wird der
Begriff Advance nocheinmal verdeutlicht.
AV
Advance
Abbildung 6.4: Horizontaler Abstand zwischen zwei Buchstaben
Den horizontalen Abstand eines Buchstabens erhält man folgendermaßen:
float advance = (glyphs.getGlyphMetrics(k)).getAdvance();
Mit dem Wert k wird angegeben, für den wievielten Buchstaben im Glyphvektor der Wert
ermittelt werden soll. Als letzter Schritt kann nun das erzeugte HFPolygon-Objekt zu
einem HFFont-Objekt hinzugefügt werden.
HFFont font = new HFFont(font_name);
font.addGlyph(polygon,currentChar,currentAdvance);
Die Methode addGlyph erhält als ersten Parameter das Polygon, welches ein Zeichen
darstellt, dann den ASCII-Wert des Zeichens und als letztes den horizontalen Abstand.
Wenn dieser Vorgang für alle gewünschten Zeichen durchgeführt wurde, ist der SWFFont einsetzbar.
6 Realisierung
6.4
78
Clipping von SKF-Dateien
Der in dieser Arbeit entwickelte Generator kann aus SKF-Dateien einen Flashfilm erstellen. Für die Nutzung der Flashfilme im Internet ist es notwendig, daß sie möglichst klein
gehalten werden, damit der Betrachter des Filmes keine großen Datenmengen herunterladen muß. Es kommen aber große Datenmengen zustande, wenn ein Wetterfilm in einer
sehr hohen Auflösung generiert wird. Daher ist es sinnvoll, einen Film mit einer sehr guten Auflösung in mehrere kleine Filme zu zerlegen, indem unnötige Informationen entfernt werden. Diesen Vorgang nennt man Clipping. Die so entstandenen einzelnen Filme
kann man dann für den Nutzer zugänglich machen, indem ein Film mit grober Auflösung
gezeigt wird und der Nutzer in diesem Film einen Ausschnitt auswählen kann. Das Zerlegen eines Filmes soll bereits in der Datenbasis, also aus dem SKF Format heraus geschehen. Der allgemeine Prozeß des Clippings wurde bereits im Kapitel 5 beschrieben. Wenn
aber ein Flashfilm geclippt werden soll, liegen nicht nur gerade Linien oder Polygone
mit geraden Kanten vor, sondern auch Polygone mit einer quadratischen Bezierkurve als
Kante, primitive Objekte (z.B. Kreise oder Ovale) und auch die einzelnen Kontrolltags,
die z.B. ein Objekt verschieben, drehen oder skalieren. Wie derartige Objekte und Strukturen geclippt werden können, wird in den nächsten Abschnitten beschrieben.
6.4.1
Clipping von primitiven Objekten
In dem Flashformat gibt es primitive Objekte, wie z.B. ein Oval, ein Rechteck oder einen
Kreis. Diese Objekte sind theoretisch auch mit Hilfe von Polygonen zu erzeugen. Wenn
aber ein Rechteck mit Hilfe eines Polygons erzeugt wird, benötigt es wesentlich mehr
Speicherplatz, da alle vier Eckpunkte anzugeben sind, beim primitven Objekt Rechteck
sind aber nur zwei Punkte nötig. Noch extremer ist die Situation bei einem Kreis oder einem Oval. Diese könnten nur mit Hilfe von Bezierkurven angenähert werden. Aus diesen
Gründen ist es sinnvoll, solche primitive Objekte zu erhalten, auch wenn sie gerade auf
einer Kante liegen und teilweise weggeclippt werden müssten, um den benötigten Speicherplatz nicht in die Höhe zu treiben. Wenn also ein primitives Objekt völlig außerhalb
des Clippingfensters liegt wird es entfernt, ansonsten wird das Objekt im Film belassen.
Dadurch kann es dazu kommen, daß ein Kreis teilweise im Clippingfenster liegt und teilweise nicht. Er würde also über die Grenzen des Filmes hinausreichen. Dies ist aber kein
Problem, da der Film in einer Webseite eingebettet ist und alles was über die Grenzen des
Filmes hinausgeht nicht sichtbar ist. Wie bestimmt man nun auf einfache und effiziente
Weise, ob ein Objekt sichtbar ist oder nicht?
Ein Weg wäre es, die Kanten des primitven Objektes zu interpolieren, was vor allem bei
Kreisen recht aufwendig wäre. Es ist aber auch möglich, die Sichtbarkeit eines primitiven Objektes über seinen Mittelpunkt zu bestimmen. Bei einem Kreis ist der Mittelpunkt
bereits bekannt, ebenso wie der Radius. In diesem Falle wird lediglich geprüft, ob der
Mittelpunkt des Kreises in dem Rechteck liegt, welches an allen Seiten um den Radius
vergrößert wurde. Ist dies der Fall, ist der Kreis zumindest teilweise sichtbar und wird
nicht aus dem Film entfernt. Die Abfrage als Programmcode sieht folgendermaßen aus:
if ( centerX>(xMin-radius) && centerX<(xMax+radius) &&
centerY > (yMin-radius) && centerY < (yMax+radius) )
6 Realisierung
79
centerX und centerY sind hierbei die Koordinaten und radius der Radius des
Kreises. xMin, yMin, xMax und yMax sind die Clippinggrenzen des Clippingrechtecks.
Falls diese if-Bedingung wahr ist, ist der Kreis zumindest teilweise sichtbar.
Ähnlich wird bei Ovalen oder Rechtecken vorgegangen, nur müssen hier der Mittelpunkt
und die Abstände des Mittelpunktes zu der Boundingbox, das ist das kleinst mögliche
Rechteck in welches das Objekt hineinpaßt, errechnet werden. Darauf erfolgt wieder ein
Vergleich wie er auch schon beim Kreis durchgeführt wurde:
int deltaX = (xmax-xmin)/2; //Abstand zu den Seiten
int deltaY = (ymax-ymin)/2;
int centerX = xmin + deltaX; //Mittelpunkt des Objektes
int centerY = ymin + deltaY;
if ( centerX > (xMin-deltaX) && centerX < (xMax+deltaX)&&
centerY > (yMin-deltaY) && centerY < (yMax+deltaY) )
xmin, ymin, xmax und ymax sind dabei die Punkte der Boundingbox des Rechtecks
oder des Ovals, welche als Attribut sofort mitgeliefert werden. Auf diese Art und Weise
ist es kein Problem primitive Objekte zu clippen. Wie sieht es aber nun mit dem Clipping
von Polygonen aus?
6.4.2
Clipping von Polygonen mit Bezierkurven
Das Clipping von Polygonen mit geraden Kanten wurde bereits im Kapitel 5.4 erläutert
und stellt somit grundsätzlich kein Problem dar. Allerdings können die Polygone in Flash
quadratische Bezierkurven als Kanten enthalten. Das Clipping dieser Kurven erfolgt vom
Grundprinzip her genauso wie bei dem Polygonclipping mit geraden Linien. Allerdings
ist hier die Schnittpunktberechnung wesentlich aufwendiger und die Berechnung des
Schnittpunktes allein reicht nicht aus. Es muß auch der Kontrollpunkt neu errechnet
werden. Wie können also Bezierkurven an einer waagerechten oder senkrechten Linie
geschnitten werden?
Eine quadratische Bezierkurve wird durch drei Punkte definiert, zwei Ankerpunkte und
einen Kontrollpunkt. Wenn diese drei Punkte zu einem Linienzug miteinander verbunden werden, verläuft die Bezierkurve in der konvexen Hülle des Linienzuges, wobei die
Bezierkurve an den Ankerpunkten dieselbe Steigung besitzt, wie die Linien in den Ankerpunkten [Deho2001] (siehe Abbildung 6.5 auf Seite 80).
Eine Bezierkurve wird stets in einer parametrisierten Form angegeben, d.h. es wird keine Funktion in Abhängigkeit von x und y angegeben, sondern eine vom Parameter t
abhängige Funktion:
P (t) = (1 − t)2 · P0 + 2 · t · (1 − t) · P1 + t2 · P2
t²[0, 1]
(1)
Somit ergeben sich folgende zwei Gleichungen:
x(t) = (1 − t)2 · x0 + 2 · t · (1 − t) · x1 + t2 · x2
y(t) = (1 − t)2 · y0 + 2 · t · (1 − t) · y1 + t2 · y2
(2)
(3)
6 Realisierung
80
P0 =(x 0,y 0)
Q01
P(t)=Q 012
P1 =(x 1,y 1)
Q12
P2 =(x 2,y 2)
Abbildung 6.5: Eine quadratische Bezierkurve
Die Darstellung einer Bezierkurve erfolgt durch den Algorithmus von de Casteljau. Die
Interpolationsformel für die Punkte P(x, y) lautet folgendermaßen [Schw1997]:
P (t) = LERP (LERP (P0 , P1 , t), LERP (P1 , P2 , t), t)
(4)
wobei LERP für Linear Interpolation steht und folgendermaßen aufgebaut ist :
LERP (a, b, t) = (1 − t) · a + t · b
(5)
Algorithmisch sieht die LERP-Funktion wie folgt aus:
private Point LERP(Point a, Point b, double t) {
double avalues[] = a.getValues();
double bvalues[] = b.getValues();
double newX = 0;
double newY = 0;
newX = (1-t)*avalues[1]+t*bvalues[1];
newY = (1-t)*avalues[2]+t*bvalues[2];
return new Point(newX, newY);
}
Diese Funktion wird innerhalb des de Casteljau Algorithmus rekursiv aufgerufen, um die
Punkte auf der interpolierten Kurve zu errechnen.
Wenn die Schnittpunkte dieser Kurve mit einer Waagerechten
y = const.k
(6)
gesucht werden, wird die Konstante k in Gleichung (3) für y(t) eingesetzt und man erhält
folgende Gleichung:
(1 − t)2 · y0 + 2 · (1 − t) · t · y1 + t2 · y2 = k
(7)
6 Realisierung
81
Diese Gleichung kann nach t aufgelöst werden:
p
y0 − y1 − 2 k · y0 − 2 · k · y1 + y12 + k · y2 − y0 · y2
t0,1 =
y0 − 2 · y1 + y2
t0,1 ²[0, 1]
(8)
Mit der Formel (8) kann nun ermittelt werden, ob und welche Schnittpunkte existieren.
Die Werte für t0,1 sind dabei nur dann gültige Lösungen, wenn sie im Intervall [0,1]
liegen, denn die Schnittpunkte sollen auf der Bezierkurve zwischen P0 und P2 liegen.
(siehe Abb. 6.5) Hierbei können die folgenden drei Fälle eintreten:
• t0,1 sind keine gültigen Lösungen. Daraus folgt, daß kein Schnittpunkt existiert.
• Im Fall t0 = t1 existiert nur ein Schnittpunkt. Dies ist der Fall, wenn die Ankerpunkte auf verschiedenen Seiten der schneidenden Linie liegen, wie in Abbildung 6.6a zu sehen ist.
• Wenn t0 6= t1 und beide Lösungen im Intervall [0,1] liegen, gibt es zwei Schnittpunkte. Diese Fälle treten ein, wenn die Ankerpunkte auf der einen und der Kontrollpunkt auf der anderen Seite der schneidenden Linie liegen. Die beiden möglichen Fälle sind in den Abbildungen 6.6b und 6.6c abgebildet.
P0
P0
P1
P2
außen
außen
P’0
P1
P’0
innen
P’2
außen
P’2
P’1
P’1
P2= P’2
(a) Ein Anker innen, einer außen
P0 = P’0
P’’
0
innen
innen
P’1
P’’
1
P2 = P’’
2
(b) Beide Anker innen, Kontrolpunkt außen
P1
(c) Beide Anker außen, Kontrollpunkt innen
Abbildung 6.6: Verschiedene Arten von Schnittpunkten mit einer Bezierkurve
Der nächste Schritt ist die Berechnung der zu t0,1 gehörigen Schnittpunkte. Sie können
durch die Formel (3) errechnet werden, indem der zuvor ermittelte t-Wert eingesetzt wird.
Eine weitere Berechnungsart ist mit Hilfe der Formel (5) möglich. Wenn nun also t0,1 existieren, erhält man die neuen Punkte P00 und P20 , indem folgende Berechnungen durchgeführt werden:
P00 = LERP (LERP (P0 , P1 , t0 ), LERP (P1 , P2 , t0 ), t0 )
P20 = LERP (LERP (P0 , P1 , t1 ), LERP (P1 , P2 , t1 ), t0 )
(9)
6 Realisierung
82
Falls man eine der Gleichungen in der Formel (9) ausmultipliziert, ist sofort ersichtlich,
daß sie mit der Gleichung in Formel (3) übereinstimmt.
Nachdem die Schnittpunkte errechnet wurden, ist es noch notwendig, den neuen Kontrollpunkt zu errechnen. Hierbei kann es zwei Fälle geben. Wenn die Bezierkurve nur
einmal geschnitten wird, also nur ein Schnittpunkt existiert, ist der Kontrollpunkt sehr
einfach zu bestimmen. Der neue Kontrollpunkt ist stets der Schnittpunkt der Tangenten,
die an P0 und P2 anliegen. Wenn nur ein Schnittpunkt existiert, bleibt einer der ursprünglichen Ankerpunkte erhalten und dementsprechend auch dessen Tangente, d.h. der neue
Ankerpunkt wird genau auf dieser Geraden liegen. Um zu ermitteln, wo sich der Schnittpunkt auf dieser Geraden befindet, ist es nur nötig, den ersten Schritt der de CasteljauInterpolation durchzuführen. Dies ist auch in der Abbildung 6.5 zu erkennen. Im ersten
Schritt wird der Punkt Q01 , im zweiten der Punkt Q12 und im dritten Schritt der Punkt
Q012 errechnet. Der Punkt Q012 ist dann der interpolierte Punkt auf der Kurve.
Beispiel
Es sei P (t) eine Bezierkurve mit den Ankerpunkten P0 und P2 sowie dem Kontrollpunkt
P1 (siehe Abbildung 6.7).
außen
innen
P1
Q12 = P’1
Q01
Q012= P’0
P2 = P’2
P0
Abbildung 6.7: Alte und neue Punkte der Bezierkurve
Es existiert lediglich ein Schnittpunkt und die Kurve soll von P0 bis zum Schnittpunkt
weggeclippt werden, so daß die neue Kurve durch P00 , P10 und P2 bestimmt wird. Durch
LERP (P0 , P1 , t0 )
(10)
wird der Punkt Q01 errechnet. Der Punkt Q12 wird mittels
LERP (P1 , P2 , t0 )
(11)
errechnet. So erhält man die Gerade Q01 Q12 . Über die beiden Berechnungen (10) und
(11) kann nun folgende Formel aufgestellt werden:
Q012 = LERP (LERP (P0 , P1 , t0 ), LERP (P1 , P2 , t0 ), t0 )
(12)
6 Realisierung
83
Dies ergibt genau den zu t0 gehörigen Kurvenpunkt, welcher der neue Ankerpunkt P00 der
Kurve ist. Q12 P2 ist die Tangente im Punkt P2 und Q01 Q12 die Tangente im Punkt P00 =
Q012 . Daraus wird sofort ersichtlich, daß Q12 der Schnittpunkt der beiden Tangenten und
somit der neue Kontrollpunkt der Bezierkurve ist. Als Veranschaulichung dient die obige
Abbildung 6.7.
Der neue Kontrollpunkt lässt sich also einfach durch die Formel:
Q12 = P10 = LERP (P1 , P2 , t0 )
(13)
errechnen. Je nachdem, wie die Kurve nun geschnitten wird, d.h. einmal oder zweimal,
bzw. ob die Ankerpunkte innen oder außen liegen, ist die Berechnung immer leicht unterschiedlich, was die einzusetzenden Werte bei der LERP Funktion angeht, aber das
Grundprinzip bleibt gleich.
So wurde eine Berechnungsmethode gefunden, die es ermöglicht, eine Bezierkurve auf
einfache Art und Weise einem Clippingprozeß zu unterwerfen.
Die Implementation des Bezierclipping wird auf Seite 86 näher erläutert und der Quellcode ist im Anhang D zu finden.
6.4.3
Clipping von Kontrolltags
In Macromedia Flash können Objekte durch Kontrolltags transformiert oder in ihrem
Aussehen verändert werden. So kann es sein, daß ein ursprünglich nicht sichtbares Objekt durch eine Translation sichtbar wird oder umgekehrt. Es stellt sich daher die Frage,
ob überhaupt ein Objekt entfernt werden darf oder nicht. Ein anderes Problem entsteht,
wenn ein Objekt durch das Clippen entfernt wurde und sich eine Transformation oder
eine Farbänderung auf dieses Objekt beziehen möchte.
Letzteres Problem wird dadurch gelöst, daß jedes Objekt, das völlig entfernt wurde, in
einen ObjectManager geschrieben wird, der sich die eindeutige ID des Objektes merken
kann. Ist es später notwendig zu erfahren, ob ein Objekt entfernt wurde oder nicht, kann
dies über die ID beim ObjektManager erfragt werden. Zum Beispiel wird beim Clippen einer Translation überprüft, ob die ID, auf welche sich die Translation bezieht, im
ObjektManager enthalten ist. Ist dies der Fall, wird diese Translation entfernt oder, falls
sich die Translation auf mehrere IDs bezieht, nur die entsprechende ID. Dieses Vorgehen
ist ohne Probleme möglich, da die Objekte stets zuerst definiert werden müssen, bevor
sich eine ID auf ein Objekt bezieht. Ganz gemäß dem Prinzip in Flash, nach dem die
Definitionstags auch immer vor den Kontrolltags stehen müssen.
Was ist aber nun mit dem umgekehrten Weg, wenn ein Objekt vielleicht nicht sichtbar
ist, es aber durch eine Transformation dazu kommen könnte? Für diesen Fall wurde in
der DTD für jedes Objekt ein Attribut clip eingefügt. Dieses Attribut ist standardmäßig
auf true gesetzt, d.h. dieses Objekt darf geclippt werden. Wenn nun der Entwickler einer SKF Datei der Meinung ist, daß dieses Objekt sich bewegt und eine Darstellung des
Objektes zwingend erforderlich ist, kann das Attribut auf false gesetzt werden und das
Objekt wird beim Clippen ignoriert.
6 Realisierung
6.4.4
84
Der Clipping-Datenfluß
Eine SKF Datei wird durch den Clippingprozeß in einzelne Dateien zerlegt. Damit das
Einlesen und Verarbeiten keine unnötige Zeit in Anspruch nimmt und es nicht nötig
ist, auf bereits vorher definierte Elemente zurückzugreifen, wird die SKF Datei über
einen SAX Parser eingelesen, was wesentlich schneller ist und weniger Arbeitsspeicher
benötigt. Dieser SAX Parser erzeugt dann Ereignisse, die von einem Handler verarbeitet werden können (siehe hierzu auch Kapitel 3.4.1). Dieser Handler wird durch eine
Clipper-Klasse repräsentiert, die die einzelnen Methoden des Handlers implementiert
und das Clippen ermöglicht. Da die Anzahl der Clippinggrenzen variieren kann, sollte
kein Clipper entwickelt werden, der an beliebig vielen Clippingrechtecken clippen kann,
sondern ein Clipper der für genau ein Clippingrechteck zuständig ist. Je nach Bedarf
werden dann die benötigten Clipper erzeugt. Dies hätte aber zur Folge, daß der Clippingprozeß für alle Grenzen einzeln durchgeführt werden müsste, also die SKF Datei
so häufig geparst werden muß, wie es Clippingrechtecke gibt, was viel zu zeitaufwendig wäre. Daher wird ein sogenannter Multiplexer zwischen SAX Parser und Clipper
geschaltet, der ebenfalls die Methoden des Handlers implementiert und die Ereignisse
des SAX-Parsers an die bei ihm eingetragenen Klassen weiterleitet (siehe auch Abb 6.8).
Der Multiplexer wird bei dem SAX Parser als Document Handler eingetragen und fängt
dadurch die vom SAX Parser erzeugten Events auf. Darauf schickt er diese Events an alle bei ihm registrierten Clipper mit den jeweiligen Clippinggrenzen weiter, welche dann
also die vollständige Information aus der SKF Datei erhalten und in die entsprechenden
Teildateien zerlegen.
SKF Clipper I
SKF Clipper II
SAX Parser
SAXMultiplexer
SKF Clipper III
SKF Clipper IV
Abbildung 6.8: Verteilung der Daten über den SAXMultiplexer
Die Klassenstruktur des SKFClippers ist im Anhang C zu finden.
6 Realisierung
6.4.5
85
Die Clipper-Objekte
Im vorangegangenen Abschnitt wurde der Datenfluß von der Datei bis zum einzelnen
Clipperobjekt beschrieben. Wie aber geht es nun weiter? Wie werden einzelne Objekte
geclippt?
Der Clipper, welcher die Daten aus der SKF Datei über den SAX Parser und den Multiplexer erhält, bekommt bei der Instanziierung die Clippinggrenzen, für die er zuständig
ist, und den Namen der Datei, in der das Ergebnis des Clippings abgelegt werden soll,
mitgeliefert. Anhand der Grenzen kann nun also der Clippingprozeß stattfinden. Die zu
clippenden Objekte sind aber zu unterschiedlich, als daß man einen einzigen Clipper entwickeln könnte, der alle Objekte mit ihren entsprechenden Sonderfällen berücksichtigt.
Daher werden spezielle Clipper eingeführt. So z.B. ein CircleClipper, welcher speziell für das Clippen von Kreisen zuständig ist.
Die verschiedenen Elemente in SKF erhalten also teilweise einen Spezialclipper. Objekte,
die nicht zu Clippen sind (z.B. ein Frame), werden über einen Defaultclipper verarbeitet,
der die Objekte unverändert wieder ausgibt. Der Clipper erkennt beim Parsen automatisch, um was für ein Element es sich handelt und leitet dann die folgenden Daten an den
entsprechenden Spezialclipper weiter. Ist der Clippingprozeß für dieses Element beendet, wird die Verantwortung für den Datenfluß wieder an den Clipper zurückgegeben, bis
erneut ein Element folgt, welches den Einsatz eines Spezialclippers notwendig macht.
Wenn allerdings das Element mit einer Flagge versehen ist, daß dieses nicht geclippt
werden darf, wird der Defaultclipper eingesetzt, der die erhaltenen Daten einfach in die
angegebene Datei schreibt.
Wie die einzelnen Spezialclipper arbeiten, hängt von dem Objekt ab, für das sie zuständig
sind. Ein Clipper für Kreise, Rechtecke oder Ovale arbeitet nach dem in Kapitel 6.4.1 beschriebenen Prinzip, ein Clipper für Kontrolltags wie in Kapitel 6.4.3 beschrieben.
Der Polygonclipper ist wesentlich komplexer als die anderen Spezialclipper. Deshalb soll
näher darauf eingegangen werden.
Wenn ein polygon-Tag vom SAX-Parser erkannt wurde und über den Multiplexer an
die einzelnen Clipper, die für ein Clippingrechteck zuständig sind, weitergeleitet wurde,
reichen diese den Datenstrom an den Polygonclipper weiter.
Um das Clipping des Polygons möglichst effizient zu gestalten, werden zunächst alle Punkte des Polygons eingelesen. Diese Punkte werden in flashweather.geomutil.Point-Objekte abgelegt und daraus ein flashweather.geomutil.Polygon-Objekt erzeugt. In diesem Objekt ist die Clippinglogik enthalten. Diese Aufteilung
wurde gewählt, da sonst der Polygonclipper zu komplex werden würde.
Ist dieser Prozeß abgeschlossen, ruft der Polygonclipper bei dem Polygon-Objekt die
clip Methode auf und der eigentliche Clippingprozeß wird ausgelöst. Dieser Clippingprozeß basiert auf dem Reentrant Polygon Clipping Algorithmus von Sutherland und
Hodgman der in Kapitel 5.4 beschrieben wurde.
In einer Schleife wird Clippinggrenze für Clippinggrenze abgearbeitet. Die Punkte werden einer Methode übergeben, die die Schnittpunkte errechnet. Dabei wird für jedes Liniensegment geprüft, ob es ein gerades Segment ist oder eine quadratische Bezierkurve.
Für beide Fälle sind spezielle Methoden implementiert worden, die die Schnittpunkte mit
den Clippinggrenzen errechnen können.
6 Realisierung
86
Die Schnittpunktberechnung bei geraden Teilstücken der Polygone basiert auf der ZweiPunkte-Gleichung
y = ((x − xa )(yb − ya )/(xb − xa )) + ya
Diese Gleichung bestimmt die über die Punkte a und b definierte Gerade. Der Schnittpunkt der Geraden mit einer waagerechten Clippinggrenze berechnet sich über die Gleichung
x = ((xb − xa ) ∗ (yclip ∗ −xa ∗))/(yb ∗ −xa ∗) + xa
(14)
wobei yclip der Y-Wert der Clippinggrenze ist. Bei einer senkrechten Clippinggrenze lautet die Gleichung
y = (xclip − xa ) ∗ (yb ∗ −ya )/(xb − xa ) + ya
(15)
wobei hier xclip der X-Wert der Clippinggrenze ist.
In der Implementation ist hierfür die im Quellcode 6.8 auf Seite 87 zu sehende Methode
calcIntersecPoints zuständig. Diese Methode erhält als Parameter den Startpunkt
der Linie, den Endpunkt, die Seite der Clippinggrenze, an der geclippt werden soll, und
den Wert, den die Clippinggrenze annimmt. In dieser Methode werden dann die Gleichungen (14) und (15) implementiert. Hierbei muß darauf geachtet werden, ob der errechnete Schnittpunkt noch innerhalb der gewünschten Grenzen liegt. Da der Ursprung des
Flash-Koordinatensystems oben links liegt, müssen die Y-Werte bei der Berechnung negiert werden, um sie aus dem mathematischen in das Flash-Koordinatensystem zu transformieren.
Falls ein Teilstück eine quadratische Bezierkurve enthält, wird der Schnittpunkt über die
im Kapitel 6.4.2 erläuterten Formeln errechnet, die in der Methode calcQuadIntersecPoints implementiert sind. Die Parameter stimmen mit denen der calcIntersecPoints-Methode überein, wobei allerdings der Endpunkt ein sogenannter QuadToPunkt sein muß. Dies ist ein Objekt der Klasse Point, das sowohl einen Anker- als auch
einen Kontrollpunkt enthält, wodurch eine quadratische Bezierkurve als Liniensegment
definiert wird.
Als erstes wird der Parameter t anhand der Formel (8) auf Seite 81 errechnet und überprüft ob kein, ein oder zwei t-Werte errechnet wurden. Dementsprechend gibt es dann
keinen, einen oder zwei Schnittpunkte. Danach werden die möglichen Fälle abgearbeitet
und die Schnittpunkte über die im Kapitel 6.4.2 hergeleiteten Formeln errechnet. Da der
Quellcode zu umfangreich ist, ist er im Anhang D zu finden.
Das Ergebnis dieses Clippingprozesses wird dann an den Polygonclipper zurückgeliefert,
der die Punkte in XML Code umwandelt und an den eigentlichen Clipper zurückliefert.
Dieser schreibt die Daten in die entsprechende Datei.
6 Realisierung
private Point[] calcIntersecPoints(Point from,
Point to,
int which,
int value) {
Point [] newPoints = null;
//Werte der Punkte abholen
double [] a = from.getValues();
double [] b = to.getValues();
if (which==1 || which==3) { //waagerechte
//Clippinggrenze
//Die Zwei-Punkte-Gleichung umformen
//und damit den Schnittpunkt errechnen
//Auf Flashkoordinaten Transformieren
x = ((b[1]-a[1])*(value*(-1)-a[2]*(-1))) /
(b[2]*(-1)-a[2]*(-1))+a[1];
//Test ob der Punkt innerhalb der Grenzen liegt
if ( (x>=a[1] && x<=b[1]) || (x<=a[1] && x>=b[1]) ) {
newPoints = new Point[1];
newPoints[0] = new Point(x,y);
}
}
else if (which==2 || which==4) { //senkrechte
//Clippinggrenze
//Die Zwei-Punkte-Gleichung umformen
//und damit den Schnittpunkt errechnen
//Auf Flashkoordinaten Transformieren
y = -1*(((x-a[1])*(b[2]*(-1)-a[2]*(-1))) /
(b[1]-a[1])+a[2]*(-1));
//Test ob der Punkt innerhalb der Grenzen liegt
if ( (y>=a[2] && y<=b[2]) || (y<=a[2] && y>=b[2]) ) {
newPoints = new Point[1];
newPoints[0] = new Point(x,y);
}
return newPoints; //Ergebnis der Intersection liefern
}
}
Quellcode 6.8: Polygon.java: Die Intersec Methode für gerade Linienstücke
87
7 Anwendung
88
7 Anwendung
In der vorliegenden Diplomarbeit wurde ein Werkzeug entwickelt, mit dem Flashfilme
über einen Generator aus dem SKF Schnittstellenformat erzeugt werden können. Hierfür
wurde eine Java SWF API erstellt, die das in C++ entwickelte Macromedia SWF-SDK
der Programmiersprache Java zugänglich macht. Zusätzlich können Dateien, die im SKF
Format vorliegen, geclippt werden. In diesem Kapitel werden kurze Beispiele zur Verwendung der Java SWF API, des Generators und des Clippingtools vorgestellt, sowie
die Voraussetzungen bezüglich der Hard- und Software erläutert. Zusätzlich wird auf die
Performance der einzelnen Tools eingegangen.
7.1
Benötigte Hard- und Software
Benötigte Hardware
Die Erstellung von Flashfilmen ist sehr speicher- aber wenig prozessorintensiv, wie sich
in einigen Tests herausgestellt hat. Diese Tests wurden auf dem Betriebssystem Linux
Version 7.1 vorgenommen. Für den Test wurden zwei Rechner eingesetzt. Der eine Rechner arbeitet mit einem Pentium III Prozessor mit einer Leistung von 850 Mhz und 128
MB Arbeitsspeicher, der andere besitzt einen Celeron Prozessor mit 633 Mhz Leistung
und 256 MB Arbeitsspeicher. Vor allem beim Erstellen eines DOM-Trees, der bei der
Generierung eines Flashfilmes eingesetzt wird, ist sehr viel Arbeitsspeicher notwendig,
da jedes einzelne Tag und Attribut im DOM als Knoten abgelegt wird. So erweitert ein
einziges Tag mit vier Attributen (z.B. beim rectangle-Tag) den DOM-Tree um fünf
Node-Objekte. Je größer die zu parsende Datei ist, desto größer ist der Arbeitsspeicherbedarf. Bei einer 3.5 MB großen SKF-Datei mit 69123 Polygonpunkten reichte bei beiden
Rechnern der Arbeitsspeicher nicht aus und das Programm wurde abgebrochen. Bei einer
1.5 MB großen Datei (31572 Punkte) pendelt sich der Speicherbedarf bei ca. 76 MB ein.
Dieses Ergebnis wurde mit dem Kommando top ermittelt. Dies hat zur Folge, daß besonders bei hochauflösenden Wetterdaten der Speicherbedarf stark ansteigt und ein Film
unter Umständen nicht mehr geparst werden kann. Die Prozessorbelastung ist allerdings
relativ gering und beläuft sich auf ca. 27% Auslastung.
Falls die Datei für die Verarbeitung zu groß ist kann der Clipper eingesetzt werden,
um diese Datei zu zerlegen. Der auf einem SAX-Parser basierende Clipper benötigt im
Durchschnitt lediglich 13 MB Arbeitsspeicher, dafür ist aber die Prozessorauslastung wesentlich höher, sie liegt bei ca. 73%.
Die Hardwareausstattung muß also entsprechend dem eingesetzten Parser ausgewählt
werden.
Benötigte Software
Alle in dieser Arbeit entwickelten Programme wurden in der plattformunabhängigen
Programmiersprache Java entwickelt. Da Sun Microsystems das Java Development Kit
7 Anwendung
89
(JDK) für Linux, Solaris und Windows implementiert hat und es auf fast alle anderen
Plattformen portiert wurde, ist kein spezielles Betriebssystem für die Erstellung eines
Flashfilms erforderlich. Die Verwendung des Generators setzt allerdings die Existenz einer grafischen Oberfläche (X11, Windows) voraus.
Folgende Software-Pakete werden für die Erzeugung und das Clippen von Flashfilmen
mit den Flashweather-Klassen benötigt und sind kostenlos im Internet verfügbar:
• Sun Java 2 SDK 1.2.2 oder höher (Java Software Development Kit)
• Apache Xerces Java Parser 1.1.3 oder höher, oder ein anderer XML-Parser, der die
Methoden der DOM-Level 2 und SAX-Level 2 Spezifikationen implementiert.
Das Java Development Kit muß auf dem verwendeten System installiert werden. Die
XML-Parser bestehen lediglich aus JAR-Dateien, die der CLASSPATH-Variablen der
Laufzeitumgebung hinzugefügt werden müssen.
7.2
Anwendung der Java SWF API
Um die Java SWF API nutzen zu können, sind einige Vorbereitungen notwendig. Zunächst muß die JAR-Datei mit dem Java Paket f3sdk.highlevel der CLASSPATHVariablen hinzugefügt werden, damit sie von der Java Virtual Machine gefunden werden
kann. Zusätzlich muß die Bibliothek mit den implementierten native-Methoden und dem
SWF-SDK in die Variable LD LIBRARY PATH eingetragen werden.
Sobald diese Vorbereitungen abgeschlossen worden sind, kann die Java SWF API in die
eigene Klasse importiert und genutzt werden.
Im Quellcode 7.1 auf Seite 90 ist das Beispiel aus Kapitel 2.4 auf Seite 19 nocheinmal
in Java implementiert worden. Dieses Programm erzeugt einen Film, der einen Kreis und
ein Quadrat enthält. Das Quadrat wird in zwei Schritten um insgesamt 60 Grad gedreht.
Ein Ausschnitt aus diesem Film ist in der Abbildung 7.1 zu sehen.
Abbildung 7.1: Ausschnitt aus einem mit der Java SWF API erstellten Flashfilm
7 Anwendung
90
import f3sdk.highlevel.*;
public class Flash {
public static void main(String args[]) {
// erzeuge einen neuen Flashfilm
HFMovie movie = new HFMovie();
// setze die Groesse auf 300*300 Pixel
// Angaben in TWIPS=>6000*6000
movie.setSize( 6000, 6000 );
// drei Bilder pro Sekunde
movie.setFrameRate( 3 );
// ein Rechteck erzeugen
HFRectangle rect = new HFRectangle(400, 400, 2400, 2400);
// einen Kreis erzeugen
HFCircle circle = new HFCircle(1600, 1000, 800);
// setze die Fuellfarben
FRGBAColor c = new FRGBAColor();
rect.setLinearFill(c.Blue_RGBA, c.Black_RGBA );
circle.setRadialFill(c.Yellow_RGBA, c.Violet_RGBA );
//Schluesselbilder erzeugen
HFFrame frame0 = movie.createFrame(0);
HFFrame frame1 = movie.createFrame(1);
HFFrame frame2 = movie.createFrame(2);
// fuege Objekte zu dem ersten Schluesselbild hinzu
frame0.addObject( rect );
frame0.addObject( circle );
// rect dreht sich um den eigenen Mittelpunkt
frame1.removeObject( rect );
rect.rotate( 30.0f );
frame1.addObject( rect );
frame2.removeObject( rect );
rect.rotate( 60.0f );
frame2.addObject( rect );
// schreibe die SWF-Datei und beende das Programm
movie.writeMovie("HFExample.swf");
}
}
Quellcode 7.1: Implementation zur Erstellung eines Flashfilms
7 Anwendung
7.3
91
Erstellen eines Flashfilmes aus einer SKF-Datei
Damit aus einer SKF-Datei ein Flashfilm generiert werden kann, muß das Java Paket flashweather.generator in die CLASSPATH-Variable eingebunden werden.
Außerdem muß die Java SWF API korrekt installiert sein. Damit der Generator letztendlich genutzt werden kann, ist noch der Xerces DOM-Parser nötig. Dieser DOM-Parser
besteht nur aus einer JAR-Datei, die ebenfalls in der CLASSPATH-Variablen enthalten
sein muß.
Nach diesen Vorbereitungen kann ein SKFXercesDOMParser instanziiert werden und
der Generierungsprozeß kann mit der Methode createFlashMovie, wie in Quellcode 7.2 zu sehen, gestartet werden. Für den Fall, daß ein Fehler bei der Generierung eines
Flashfilms auftritt, wird die dabei entstehende Exception abgefangen und ausgegeben.
import f3sdk.highlevel.HFMovie;
import flashweather.generator.SKFXercesDOMParser;
import flashweather.generator.GeneratorException;
/**
* Klasse zum generieren eines Flashfilmes aus einer
* SKF-Datei
*/
public class Generator {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("Usage: java Generator filename");
System.exit(1);
}
//Parser instanziieren
SKFXercesDomParser parser = new SKFXercesDomParser(true);
//Generierungsprozess starten
try {
HFMovie movie = parser.createFlashMovie(args[0]);
}
catch(GeneratorException e) {
System.err.println("Fehler bei der Generierung "
+"des Flashfilms: "+e);
System.exit(1);
}
//Den Film in eine Datei schreiben
movie.writeMovie(args[0]+".swf");
}
}
Quellcode 7.2: Nutzung des SKF-Generators
7 Anwendung
7.4
92
Clippen einer SKF-Datei
Um eine SKF-Datei zu clippen ist das Paket flashweather.clipper erforderlich.
Damit es genutzt werden kann, muß es zuvor in der CLASSPATH-Variablen eingetragen
werden. Außerdem ist ein beliebiger SAX-Parser erforderlich, der im allgemeinen nur
aus einer JAR-Datei besteht, auf welche selbstverständlich auch im Klassenpfad verwiesen werden muß.
Nach diesen Vorbereitungen kann der Clipper genutzt werden, indem zunächst der SAXParser instanziiert wird. Als Handler wird der SAXMultiplexer eingetragen, welcher
bei seiner Instanziierung eine beliebige Anzahl von SKFClipper-Objekten mitgeliefert bekommt. Diese Clipperobjekte erhalten bei ihrer Erzeugung über den Constructor
die Clippinggrenzen für die sie zuständig sind und den Namen der Datei in der sie die
geclippten Daten ablegen.
Im Quellcode 7.3 ist nocheinmal die Nutzung des Clippers zu sehen.
import org.apache.xerces.parsers.SAXParser
import flashweather.clipper.*;
/**
* Klasse zur Initiierung des Clippingprozesses
*/
public class Clipper {
public static void main(String args[]) {
//zwei Clipper Objekte instanziieren
SKFClipper [] clipper = new SKFClipper[2];
clipper[0] = new SKFClipper("datei0",0,0,350,500);
clipper[1] = new SKFClipper("datei1",350,0,700,500);
//Parser instanziieren,
//SAXMultiplexer als Handler registrieren,
//Clipping starten
try {
DefaultHandler handler = new SAXMultiplexer(clipper);
SAXParser parser = new SAXParser();
parser.setContentHandler(handler);
parser.parse(args[0]);
}
catch (Exception e)
{ e.printStackTrace(System.err); }
}
}
Quellcode 7.3: Nutzung des SKF-Clippers
7 Anwendung
93
Zur Veranschaulichung soll die im Quellcode 7.4 enthaltene SKF-Datei geclippt werden.
<?xml version=’1.0’ encoding=’UTF-8’?>
<!DOCTYPE skf SYSTEM ’skf.dtd’>
<skf width="4000" height="3000" rate="10">
<frame number="0" ID="karte0">
<backgroundRGBA r="255" g="255" b="255" a="0"/>
<!-- Graues Rechteck in der Groesse des Clippingfensters
erstellen -->
<shape>
<rectangle ID="r" xmin="200" ymin="800"
xmax="3100" ymax="2350">
<setFillColor name="Grey"/>
</rectangle>
</shape>
<!-- Zu cippendes Polygon mit gruener radialer
Fuellung -->
<shape>
<polygon ID="p1" originX="1000" originY="1000">
<addCurvedLine anchorX="2000" anchorY="1000"
controlX="1300" controlY="500"/>
<addCurvedLine anchorX="2000" anchorY="2500"
controlX="2400" controlY="2250"/>
<addStraightLine x="1800" y="2300"/>
<addCurvedLine anchorX="500" anchorY="2500"
controlX="2500" controlY="2000"/>
<addStraightLine x="1000" y="1000"/>
<setRadialFillColorsRGBA centerR="51" centerG="255"
centerB="51" centerA="127"
outR="0" outG="0"
outB="0" outA="127"/>
</polygon>
</shape>
</frame>
</swf>
Quellcode 7.4: Zu clippende SKF-Datei
7 Anwendung
94
In der SKF-Datei ist ein graues Rechteck enthalten, welches die Clippinggrenze darstellt
und ein mit grünem Farbton radial gefülltes Polygon, welches anhand der vorgegebenen
Clippinggrenzen geclippt werden soll.
In Abbildung 7.2 ist das ungeclippte Objekt zu sehen, welches Kante für Kante geclippt
wird.
Abbildung 7.2: Einzelne Schritte des Clippings
7 Anwendung
7.5
95
Beispielfilme
Im Folgenden sollen einige Beispiele für Wetterfilme gegeben werden. Die in [Stark2001]
vektorisierten Wetterdaten werden erst in das SKF-Format konvertiert und dann durch
den Generator in einen Flashfilm umgewandelt. Bevor der Generierungsprozess einsetzt,
wird gegebenenfalls noch der SKF-Clipper genutzt, um lediglich einen Ausschnitt aus
den original Wetterdaten zu erhalten.
Abbildung 7.3: Momentaufnahme der vorhergesagten Temperaturwerte für Afrika
7 Anwendung
Abbildung 7.4: Mit dem Clipper erzeugten Ausschnitt aus der Karte 7.3
96
7 Anwendung
97
Abbildung 7.5: Prognostizierte Regenmengen für Europa
8 Mögliche Einsatzgebiete
98
8 Mögliche Einsatzgebiete
Der Generator wurde für die Visualisierung von XML basierten Daten entwickelt. Exemplarisch wurden in der vorliegenden Arbeit Wetterdaten visualisiert, was aber nur eine
Einsatzmöglichkeit darstellt. Daneben sind noch viele weitere Einsatzgebiete denkbar:
Einerseits Gebiete, in denen hochwertige Grafiken erforderlich sind und andererseits Gebiete, in denen zeitbasierte Änderungen stattfinden, die visualisiert werden sollen.
Die hier angeführten Beispiele sollen lediglich eine Anregung bieten, in welchen Anwendungsgebieten der Generator auch genutzt werden kann.
Hochwertige Darstellung von Grafiken im Internet
Im Bereich der hochwertigen Darstellung von Grafiken stelle man sich z.B. eine mit einer
beliebigen Software erstellte technische Zeichnung vor, die qualitativ hochwertig im Internet auf einer Webseite präsentiert werden soll. Mit einer einfachen GIF-Grafik ist die
Darstellung zwar grundsätzlich möglich, aber eine Vergrößerung des Bildes ohne Qualitätsverlust nicht, was aber gerade bei einer komplexen Zeichnung sinnvoll sein kann.
Wenn nun aber diese Daten direkt von der Software aus in das SKF Format geschrieben
oder aber durch eine Konvertierung in dieses Format umgewandelt werden, könnte aus
dieser Zeichnung mit dem Generator ein Flashfilm erzeugt werden. Dieser Film wäre einerseits platzsparend und andererseits qualitativ hochwertig.
Ein weiterer Punkt wären alle Arten von Karten, insbesondere Landkarten. In Geographischen Informations Systemen (GIS) sind die Daten von Landkarten in vektorisierter
Form vorhanden. Diese Daten müßten ausgelesen und in das SKF Format konvertiert
werden, wodurch dann die Umsetzung in einen Flashfilm möglich wäre.
Animationen statistischer oder simulierter Daten
Eine weiteres Einsatzgebiet wäre die Visualisierung von Daten, welche sich über die Zeit
ändern. Man stelle sich vor, es sollen die Ergebnisse eines Räuber-Beute-Modells22 im
Internet präsentiert werden. Hierbei ändern sich zwei Größen über die Zeit, was mit einer
statischen Grafik nur schwer dargestellt werden kann. Wenn aber die Daten in das SKF
Format umgewandelt werden würden, kann so ein Flashfilm erstellt werden, der die Ergebnisse als Animation darstellt. Auch der Einsatz bei Banken wäre denkbar, wenn man
sich vorstellt, daß komplexe Statistiken visualisiert werden sollen, um z.B. eine Analyse
von zeitbasierten Vorgängen zu ermöglichen.
22
Ein Modell, bei dem die Population von Räubern und deren Beute über die Zeit simuliert wird
99
III Resumée und Ausblick
9 Resumée
100
9 Resumée
Diese Diplomarbeit stellt einen “Generator für Macromedia Flash zur interaktiven Visualisierung XML basierter Daten“ zur Verfügung. Für die Datenvisualisierung wurde das
Macromedia Flashformat gewählt, da es gegenüber pixelbasierten Formaten, wie z.B.
JPEG oder GIF erhebliche Vorteile besitzt. Zudem können Flashfilme von ca. 90% der
Internetnutzer betrachtet werden und das Format ist – Dank seiner großen Verbreitung –
zu einem defacto Standard geworden. Macromedia stellt außerdem ein kostenloses Software Development Kit zur Verfügung, mit dem Flashfilme programmgesteuert erzeugt
werden können.
Beispielhaft wurden von meinem Kommilitonen Benjamin Stark zur Verfügung gestellte
vektorisierte Wetterprognosedaten des Deutschen Wetterdienstes in Macromedia Flashfilmen visualisiert.
Bei der Visualisierung von Wetterdaten stellte sich heraus, daß bei der Erstellung hochauflösender Wetterkarten die Flashfilme trotz des platzsparenden Flashformates sehr
groß werden, so daß sie nicht schnell genug aus dem Internet geladen werden können.
Daher wurde ein SKF-Clipper entwickelt, der die im SKF-Format vorliegenden Daten in
einzelne Ausschnitte zerlegen kann.
Aus diesem Grund ist die Diplomarbeit in vier Teile gegliedert:
1. Entwicklung einer auf dem Macromedia C++ SWF-SDK basierenden Java API,
mit der Flashfilme erstellt werden können
2. Definition eines Schnittstellenformates aus dem beliebige Filme generiert werden
können
3. Entwicklung eines Generators, der aus dem Schnittstellenformat Flashfilme erstellen kann.
4. Implementation eines Clippers, der die im Schnittstellenformat vorliegenden Dateien in einzelne Ausschnitte zerlegen kann.
Um die Verwendung des SWF-SDKs von Macromedia auch Java Programmierern zugänglich zu machen wurde eine Java SWF API entwickelt, die auf dem SWF-SDK basiert.
Hierfür wurde das Java Native Interface genutzt, welches eine Kommunikation von Javaprogrammen mit in anderen Programmiersprachen entwickelten Bibliotheken ermöglicht.
Die Java SWF API bildet die Grundlage für den Generator, der diese API nutzt, um Flashfilme zu erstellen.
Als Dateneingabeformat für den Generator konnten nicht beliebige Dateiformate unterstützt werden, so daß ein XML basiertes Schnittstellenformat entwickelt wurde. XML
ist ein noch recht junger Standard des W3C, der aber als der zukünftige Standard für
den Datenaustausch eingeschätzt wird. Zudem stehen standardisierte Programme zur
Verfügung, die XML-Dateien einlesen, überprüfen und einem Programm zur Verfügung
stellen können.
Es wurde ein Visualisierungsprozeß entwickelt, der fast beliebige in XML definierte Daten verarbeiten kann, wenn diese in das Schnittstellenformat umgewandelt werden. Hierzu muß lediglich ein Filter implementiert werden, der die Daten von einem XML Format
9 Resumée
101
in ein anderes konvertieren kann. Als Grundlage hierzu kann die eXtensible Stylesheet
Language (XSL) genutzt werden, mit der Regeln für die Konvertierung definiert werden
können. Für XSL gibt es bereits leistungsstarke, kostenlose Programme, die anhand der
XSL Definitionen diesen Konvertierungsvorgang durchführen können.
So wurde eine Softwarebibliothek entwickelt, die es ermöglicht auf komfortable Art und
Weise aus fast beliebigen nicht nur ausschließlich auf XML basierenden Daten, einen
Flashfilm zu generieren.
10 Ausblick
102
10 Ausblick
Es wurde eine Java API entwickelt, mit der es möglich ist, einen Flashfilm in Java zu
programmieren. Außerdem wurde ein Dateiformat für Flashfilme zur Verfügung gestellt
und ein Generator entwickelt, der dieses Dateiformat in einen Flashfilm umsetzen kann,
damit nicht immer ein neues Programm geschrieben werden muß, wenn eine Änderung
im Layout des Filmes vorgenommen werden soll. Als letztes wurde dann noch ein Clipper implementiert, der die im SKF Format vorliegenden Filme zerlegen kann.
Obwohl dies bereits eine umfangreiche Sammlung darstellt, um Flashfilme auf komfortable Art und Weise zu generieren und sie bereits vor der Generierung zu manipulieren,
ergaben sich während der Bearbeitung des Themas weitere Ideen, wovon im Folgenden
einige vorgestellt werden sollen und erklärt werden soll, warum diese nicht mehr in die
Diplomarbeit aufgenommen wurden.
Entwicklung eines eigenen SDKs
Der in dieser Arbeit entwickelte Generator basiert auf dem SWF-SDK von Macromedia, wodurch gewisse Einschränkungen gegeben sind. So werden vom SWF-SDK aus
nicht alle Features von Flash, wie z.B. einige Actionscript Funktionen unterstützt und
das SWF-SDK ist teilweise fehlerhaft.
Aus diesen, aber auch aus lizenzrechtlichen Gründen wäre es sinnvoll, das SWF-SDK
selbst zu entwickeln, was zu Beginn der Arbeit auch versucht wurde, da es zu dem Zeitpunkt das SWF-SDK noch nicht gab. Es stellte sich aber schnell heraus, daß dies zu
zeitaufwendig sein würde, so daß sofort nach Erscheinen des SWF-SDKs die Arbeit an
einem eigenen SDK abgebrochen wurde.
Entwicklung einer grafischen Benutzerschnittstelle
Um die Nutzung der Flashweather-Bibliotheken komfortabler zu gestalten, wäre es denkbar eine grafische Benutzerschnittstelle zu entwickeln, die alle entwickelten Tools ansteuern kann. Insbesondere könnten dann auch Webseiten generiert werden, in die die Filme
direkt eingebunden werden könnten.
Speziell für die Erstellung der Wetterfilme könnten Konfigurationen vorgenommen werden, um einzelne Filme übereinander zu legen und es dem Betrachter über Schaltflächen
zu ermöglichen gezielt bestimmte Wetterdaten auszuwählen. Hierfür sind jedoch einige ActionScript-Funktionen nötig, die vom SWF-SDK von Macromedia noch nicht unterstützt werden.
Morphing
Im Flash Studio, der Entwicklungsumgebung für Flashfilme von Macromedia, ist es
möglich Grafikobjekte fast stufenlos ineinander übergehen zu lassen, so daß Objekte
10 Ausblick
103
nicht sprunghaft an einem anderen Ort erscheinen oder stufenweise ineinander überblendet werden, so wie bei den in dieser Arbeit entstandenen Wetterfilmen. Die stündlichen
Bilder ändern sich sehr ruckartig, was durch einen Morphingalgorithmus unterbunden
werden könnte.
Diese Morphing “ genannte Technik erfordert jedoch die genaue Kenntnis, welches
”
Grafikobjekt in welches Grafikobjekt des nachfolgenden Animationsbildes übergeht. Hierzu sind sehr aufwendige Berechnungen nötig, da Objekte sich auflösen oder aus dem
nichts entstehen können oder völlig unterschiedliche Objekte ineinander überführt werden sollen. Daher wurden weitere Überlegungen zu diesem Thema vorerst zurückgestellt.
Scalable Vector Grafik
Das Macromedia Flashformat wurde aus den in Kapitel 2 angegebenen Gründen gewählt.
In der letzten Zeit wurde aber ein Vektorgrafikformat entwickelt, welches wesentlich
mehr Designmöglichkeiten bietet als das Flashformat. Dieses neue Format nennt sich
Scalable Vector Graphics (SVG) und wurde unter der Leitung des W3C von zahlreichen
Unternehmen entwickelt, wie z.B.Adobe, Apple, Autodesk, BitFlash, Corel, HP, IBM,
ILOG, Macromedia, Microsoft, Netscape, OASIS, Quark, RAL, Sun, Visio, Xerox und
einige weitere mehr. Diese große Zahl von Unternehmen zeigt bereits deutlich, welches
Interesse an einem neuen Grafikstandard für das Internet besteht und sogar Macromedia
selbst ist an der Entwicklung beteiligt, obwohl dieser Grafikstandard ein starker Konkurrent zum hauseigenen Flashformat werden könnte.
Im folgenden sollen einige Punkte aufgezählt werden, die SVG bietet:
• SVG ist als plattform- und browserunabhängiger Standard konzipiert
• SVG ist ein XML basiertes Format und bietet damit auch alle Vorteile von XML
• Objekte werden in SVG zeitlich koordiniert bewegt
• SVG unterstützt ein umfangreiches auf JavaScript basierendes Eventhandling
• Texte können mit beliebigen Filtern versehen und somit verfremdet werden, wobei
der Text als solcher erhalten bleibt. Dies hat zur Folge, daß ein SVG Film nach
Text durchsucht und mit Copy&Paste herauskopiert werden kann
• SVG unterstützt photoshopartige Filter Effekte
• Das Format ist sowohl komprimiert als auch unkomprimiert einsetzbar
Diese vielen Möglichkeiten haben aber den großen Nachteil, daß der SVG-Player dies
auch alles umsetzen können muß und er ist daher mit 3MB sehr groß. Dies könnte den
potentiellen Nutzer abschrecken, dieses Plugin für seinen Browser aus dem Internet zu
laden. Zudem ist SVG bisher kaum verbreitet, da das SVG-Format bisher nur als Candidate Recommendation verabschiedet wurde [W3C2001e]. Es befindet sich somit noch
in der letzten Vorstufe zu einem offenen Standard und ist noch nicht definitiv festgeschrieben worden. Dies hält Entwickler bisher davon ab Software, die dieses Format
10 Ausblick
104
unterstützt, zu entwickeln. Allerdings ist es durch die Vielzahl der Unternehmen die an
diesem Standard mitarbeiten möglich, daß bald die ersten SVG Anwendungen auf den
Markt kommen werden und unter Umständen unterstützen die nächsten Browser von Microsoft oder Netscape dieses Format, so daß kein zusätzliches Plugin installiert werden
muß.
SVG könnte der neue Standard für Vektorgrafiken im Internet werden und vielleicht
sogar das Flashformat ablösen. Da dies aber noch eine ungewisse Zeit dauern kann
und momentan Macromedia Flash das am weitesten verbreitete und populärste Vektorgrafikformat für das Internet ist, wurde keine SVG Unterstützung implementiert.
A Das SKF Dateiformat
A
105
Das SKF Dateiformat
Der Generator setzt SKF-Dateien in einen Flashfilm um. Die Erstellung des Formates
wurde in Kapitel 6.2.1 näher beschrieben. Die dabei entstandene Datentyp-Definition
des SKF-Formates ist im Folgenden angegeben.
<?xml encoding="ISO-8859-1"?>
<!-SKF Markup Language
Version 05.03.2001
Author Ralf Kunze
This is the Markup Language for the SKF Generator.
The tags represents the objects in the Macromedia
Flash format.
All cordinates and sizes are TWIP values.
One TWIP is 1/20 pixel.
-->
<!ENTITY % Long
<!ENTITY % Integer
<!ENTITY % Float
"CDATA">
"CDATA">
"CDATA">
<!-- predefined colorvalues -->
<!ENTITY % ColorName "(Aquamarine|BakersChocolate|Black|
Blue|BlueViolet|Brass|Bright|
Bronze|BronzeII|Brown|CadetBlue|
CoolCopper|Copper|Coral|CornFlowerBlue|
Cyan|DarkBrown|DarkGreen|DarkGreenCopper|
DarkOliveGreen|DarkOrchid|DarkPurple|
DarkSlateBlue|DarkSlateGrey|DarkTan|
DarkTurquoise | DarkWood | DimGrey |
DustyRose | Feldspar | Firebrick |
ForestGreen | Gold | Goldenrod | Green |
GreenCopper | GreenYellow | Grey |
HunterGreen | IndianRed | Khaki |
LightBlue | LightGrey | LightWood |
LimeGreen | Magenta | MandarianOrange |
Maroon | MediumAquamarine | MediumBlue |
MediumGoldenrod | MediumOrchid |
MediumSeaGreen | MediumSlateBlue |
MediumSpringGreen | MediumTurquoise |
MediumVioletRed | MediumWood |
A Das SKF Dateiformat
106
MidnightBlue | NavyBlue | NeonBlue |
NeonPink | NewMidnightBlue | NewTan |
OldGold | Orange | OrangeRed | Orchid |
PaleGreen | Pink | Plum | Quartz | Red |
RichBlue | Salmon | Scarlet | SeaGreen |
SemiSweetChocolate | Sienna | Silver |
SkyBlue | SlateBlue | SpicyPink |
SpringGreen | SteelBlue | SummerSky | Tan |
Thistle | Turquoise | VeryDarkBrown |
VeryLightGrey | Violet | VioletRed |
Wheat | White | Yellow | YellowGreen |
WeatherML_00000000 | WeatherML_58E0E0E0 |
WeatherML_580050A0 | WeatherML_880050A0 |
WeatherML_88E0E0E0 | WeatherML_A00050A0 |
WeatherML_A0E0E0E0 | WeatherML_B0E0E0E0 |
WeatherML_C0F040F0 | WeatherML_C0C01010 |
WeatherML_C0F02010 | WeatherML_C0F07010 |
WeatherML_C0F0B010 | WeatherML_C0E0E0E0 |
WeatherML_C00050A0 | WeatherML_C080E030 |
WeatherML_C00000A0 | WeatherML_C050F070 |
WeatherML_C000F0B0 | WeatherML_C000F0F0 |
WeatherML_C000C0F0 | WeatherML_C000A0F0 |
WeatherML_C00070F0 | WeatherML_C02050F0 |
WeatherML_C07040F0 | WeatherML_C0C040F0 |
WeatherML_D08800A0 | WeatherML_E8E0E0E0 |
WeatherML_FF000000 | WeatherML_FF505050 |
WeatherML_FFFFFFFF | WeatherML_FF80B040 |
WeatherML_FF0050A0 | WeatherML_FF909090 |
WeatherML_FFB0B0B0 | WeatherML_FFC0C0C0 |
WeatherML_F8D0D0D0)">
<!-- all possible fillstyles -->
<!ENTITY % FillStyles "( (setFillColor| setFillColorRGBA|
setLinearFillColors |
setLinearFillColorsRGBA |
setRadialFillColors |
setRadialFillColorsRGBA),
lineColor|lineColorRGBA)? )">
<!-- all possible transformations -->
<!ENTITY % Transformations "translate| rotate | scale">
<!-- define a flashmovie
a flashmovie contains only frames -->
<!ELEMENT swf ( (frame)* )>
A Das SKF Dateiformat
107
<!ATTLIST swf rate
%Long;
"12"
width %Integer; "800"
height %Integer; "600" >
<!-- define a frame in a flashmovie -->
<!ELEMENT frame ((background|backgroundRGBA)?,
(shape|getURL|goToFrame|play|stop|
button|sound|defineFont|
translate|rotate|scale|removeObject|
placeObject|placeClonedObject|
%ReFillStyles; )* )>
<!ATTLIST frame ID
ID
#REQUIRED
number %Long; #REQUIRED>
<!-- tag for the background color -->
<!ELEMENT backgroundRGBA EMPTY>
<!ATTLIST backgroundRGBA r %Integer; #REQUIRED
g %Integer; #REQUIRED
b %Integer; #REQUIRED
a %Integer; #REQUIRED>
<!ELEMENT background EMPTY>
<!ATTLIST background name %ColorName; #REQUIRED>
<!-- define a shape -->
<!ELEMENT shape (oval|circle|circle2|polygon|
rectangle|bitmap|text) >
<!ATTLIST shape depth
%Integer;
#IMPLIED
lineWidth %Integer;
#IMPLIED
clip
(true|false) "true">
<!-- linecolor tags -->
<!ELEMENT lineColorRGBA EMPTY>
<!ATTLIST lineColorRGBA r %Integer;
g %Integer;
b %Integer;
a %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
<!ELEMENT lineColor EMPTY>
<!ATTLIST lineColor name %ColorName; #REQUIRED>
<!-- define a font to use it later. The characters
wich are used later have to put into the text tag -->
A Das SKF Dateiformat
108
<!ELEMENT defineFont (#PCDATA)>
<!ATTLIST defineFont fontname CDATA
bold
(true|false)
italic
(true|false)
ID
ID
#REQUIRED
"false"
"false"
#REQUIRED>
<!-- text uses a previous specified font
or uses a font name and style (bold/italic).
The other attributes can be used for both ways -->
height, xCoord and yCoord in twips -->
<!ELEMENT text EMPTY>
<!ATTLIST text ID
ID
#REQUIRED
data
CDATA
#REQUIRED
font
CDATA
#IMPLIED
usefont IDREF
#IMPLIED
bold
(true|false)
"false"
italic (true|false)
"false"
height %Integer;
#IMPLIED
xCoord %Integer;
#REQUIRED
yCoord %Integer;
#REQUIRED>
<!-- now all possible shape objects -->
<!ELEMENT oval %FillStyles;>
<!ATTLIST oval ID
ID
#REQUIRED
xmin %Integer; #REQUIRED
ymin %Integer; #REQUIRED
xmax %Integer; #REQUIRED
ymax %Integer; #REQUIRED>
<!ELEMENT circle %FillStyles;>
<!ATTLIST circle ID
ID
centerX %Integer;
centerY %Integer;
radius %Integer;
<!ELEMENT circle2 %FillStyles;>
<!ATTLIST circle2 ID
ID
xmin %Integer;
ymin %Integer;
xmax %Integer;
ymax %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
<!ELEMENT polygon ( (addStraightLine|addCurvedLine)*,
%FillStyles; )>
<!ATTLIST polygon ID
ID
#REQUIRED
A Das SKF Dateiformat
109
originX %Float; #REQUIRED
originY %Float; #REQUIRED>
<!-- add straight or curved lines to a polygon -->
<!ELEMENT addStraightLine EMPTY>
<!ATTLIST addStraightLine x %Float; #REQUIRED
y %Float; #REQUIRED>
<!ELEMENT addCurvedLine EMPTY>
<!ATTLIST addCurvedLine controlX
controlY
anchorX
anchorY
%Float;
%Float;
%Float;
%Float;
<!ELEMENT rectangle %FillStyles;>
<!ATTLIST rectangle ID
ID
xmin %Integer;
ymin %Integer;
xmax %Integer;
ymax %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
<!-- define the fillstyles -->
<!ELEMENT setFillColorRGBA EMPTY>
<!ATTLIST setFillColorRGBA r %Integer;
g %Integer;
b %Integer;
a %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED>
<!ELEMENT setFillColor EMPTY>
<!ATTLIST setFillColor name %ColorName; #REQUIRED>
<!ELEMENT setLinearFillColorsRGBA EMPTY>
<!ATTLIST setLinearFillColorsRGBA
leftR
%Integer;
leftG
%Integer;
leftB
%Integer;
leftA
%Integer;
rightR %Integer;
rightG %Integer;
rightB %Integer;
rightA %Integer;
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
A Das SKF Dateiformat
110
xCenter %Integer; #IMPLIED
yCenter %Integer; #IMPLIED >
<!ELEMENT setLinearFillColors EMPTY>
<!ATTLIST setLinearFillColors
leftColor %ColorName;
rightColor %ColorName;
xCenter
%Integer;
yCenter
%Integer;
<!ELEMENT setRadialFillColorsRGBA EMPTY>
<!ATTLIST setRadialFillColorsRGBA
centerR %Integer;
centerG %Integer;
centerB %Integer;
centerA %Integer;
outR
%Integer;
outG
%Integer;
outB
%Integer;
outA
%Integer;
xCenter %Integer;
yCenter %Integer;
#REQUIRED
#REQUIRED
#IMPLIED
#IMPLIED>
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#REQUIRED
#IMPLIED
#IMPLIED>
<!ELEMENT setRadialFillColors EMPTY>
<!ATTLIST setRadialFillColors
centerColor %ColorName;
outColor
%ColorName;
xCenter
%Integer;
yCenter
%Integer;
<!-- actionscript tags -->
<!ELEMENT getURL EMPTY>
<!ATTLIST getURL url
CDATA
target CDATA
ID
ID
event %Integer;
#REQUIRED
#IMPLIED
#REQUIRED
#IMPLIED>
<!ELEMENT goToFrame EMPTY>
<!ATTLIST goToFrame frame %Integer; #REQUIRED
ID
ID
#REQUIRED
event %Integer; #IMPLIED>
<!ELEMENT play EMPTY>
#REQUIRED
#REQUIRED
#IMPLIED
#IMPLIED>
A Das SKF Dateiformat
<!ATTLIST play ID
ID
#REQUIRED
event %Integer; #IMPLIED>
<!ELEMENT stop EMPTY>
<!ATTLIST stop ID
ID
#REQUIRED
event %Integer; #IMPLIED>
<!-- just a button -->
<!ELEMENT button (getURL|goToFrame|play|stop)*>
<!ATTLIST button ID
ID
#REQUIRED
depth
%Long; #IMPLIED
shapeID_1 IDREF #REQUIRED
shapeID_2 IDREF #REQUIRED
shapeID_3 IDREF #REQUIRED
shapeID_4 IDREF #REQUIRED
clip
(true|false) "true">
<!-- now all transformations.
The coordinates are used in twips -->
<!ELEMENT translate EMPTY>
<!ATTLIST translate USE IDREFS
#REQUIRED
x
%Integer; #REQUIRED
y
%Integer; #REQUIRED>
<!ELEMENT rotate EMPTY>
<!ATTLIST rotate USE
IDREFS #REQUIRED
degree %Float; #REQUIRED>
<!ELEMENT scale EMPTY>
<!ATTLIST scale USE IDREFS #REQUIRED
x
%Float; #REQUIRED
y
%Float; #REQUIRED>
<!-- remove an object -->
<!ELEMENT removeObject EMPTY>
<!ATTLIST removeObject USE IDREFS #REQUIRED>
<!-- place a removed object
or place it twice or often -->
<!ELEMENT placeObject EMPTY>
<!ATTLIST placeObject USE IDREF #REQUIRED>
111
A Das SKF Dateiformat
<!-- place a predefined object as a new one -->
<!ELEMENT placeClonedObject EMPTY>
<!ATTLIST placeClonedObject USE
IDREF #REQUIRED
cloneID ID
#REQUIRED>
112
B Die Java SWF API
B
113
Die Java SWF API
In dieser Arbeit wurde ein Application Programming Interface (API) zur Erstellung von
Flashfilmen erzeugt. Diese API basiert auf den Highlevel-Klassen des Macromedia SWF
Software Development Kit (SDK). Um diese API nutzen zu können ist die auf der CD beigefügte C++ Bibliothek notwendig. Die Dokumentation der API ist auf der beigelegten
CD im Verzeichnis /docs/f3sdk enthalten.
f3sdk.highlevel
Dieses Paket enthält die Klassen der Java SWF API.
java.lang
Object
Color
FRGBAColor
HFAction
HFMovie
HFFont
HFObject
HFShape
HFCircle
HFActionGoToFrame
HFOval
HFActionStop
Klasse
HFButton
HFActionGetURL
HFActionPlay
extends
HFFrame
HFRectangle
HFPolygon
HFText
HFEditText
C Flashweather-Bibliotheken
C
114
Flashweather-Bibliotheken
Die FlashWeather-Bibliothek gliedert sich in den Generator, der sich in dem Java-Paket
flashweather.generator befindet, den Clipper, der im flashweather.clipper Paket enthalten ist und den dazugehörigen geometrischen Objekten, die im Paket
flashweather.geomutil enthalten sind. Die mit Javadoc erzeugte Dokumentation
der einzelnen APIs ist auf der beigefügten CD-ROM im Verzeichnis /docs/flashweather
zu finden und kann mit einem Webbrowser betrachtet werden. Die folgenden Grafiken
veranschaulichen die Klassenhierarchie innerhalb der einzelnen Pakete.
flashweather.generator
Dieses Paket enthält die Klassen des Generators.
java.lang.
Object
Pskf
Pbackground
Ptranslate
java.lang.
Throwable
java.lang.
Pframe
PbackgroundRGBA
Protate
Pshape
PsetFillColor
Pscale
Prectangle
PsetFillColorRGBA
PplaceObject
Pcircle
PsetLinearFillColors
PremoveObject
Pcircle2
PsetLinearFillColorsRGBA
Poval
PsetRadialFillColors
Pstop
Ppolygon
PsetRadialFillColorsRGBA
Pplay
Exception
GeneratorException
ObjectManager
StoringManager
PaddCurvedLine
PaddStraightLine
PdefineFont
Ptext
PlineColor
PgoToFrame
PgoToURL
implements
extends
Klasse
ParsingObject
Interface
C Flashweather-Bibliotheken
115
flashweather.geomutil
Dieses Paket enthält die Klassen der geometrischen Objekte, die für den Clipper erforderlich sind.
java.lang
Object
Point
extends
Polygon
Klasse
flashweather.clipper
Dieses Paket enthält die Klassen die zum Clippen einer SKF-Datei erforderlich sind.
java.lang.
org.xml.sax.helpers
Object
DefaultHandler
ObjectManager
SAXMultiplexer
ObjectClipperImpl
ObjectClipper
SKFClipper
ButtonClipper
CircleClipper
Circle2Clipper
DefaultClipper
OvalClipper
PolygonClipper
extends
implements
RectangleClipper
Abstrakte
Klasse
ShapeClipper
Interface
TextClipper
Klasse
D Die calcQuadIntersec Methode der Klasse Polygon
D
116
Die calcQuadIntersec Methode der Klasse Polygon
Das Clipping von quadratischen Bezierkurven ist einer der wesentlichen Algorithmen
beim Clippen von SKF-Dateien. Um die mathematische Herleitung im Kapitel 6.4.2 und
die Erläuterung der Implementation auf Seite 6.4.5 mit Quellcode zu belegen, ist im Folgenden die Methode calcQuadIntersecPoints angeführt, die die Schnittpunkte
einer quadratischen Bezierkurve mit einer Clippingkante errechnet. Da der Quellcode
sehr umfangreich ist wurde auf die Fehlerbehandlung verzichtet und es wird nur das Clipping an der oberen waagerechten Kante dargestellt. Die Vorgehensweise für die anderen
Clippingkanten ist analog implementiert.
private Point[] calcQuadIntersecPoints(Point from,
Point to,
int which,
int value) {
double x0=0, y0=0, x1=0, y1=0, x2=0, y2=0;
boolean doubleT = false;
double fromValues[] = from.getValues();
double toValues[]
= to.getValues();
x0=fromValues[1]; y0=fromValues[2];
x1=toValues[3]; y1=toValues[4];
x2=toValues[1]; y2=toValues[2];
Point P0 = new Point(x0,y0);
Point P1 = new Point(x1,y1);
Point P2 = new Point(x2,y2);
double t0=0, t1=0;
if (which==YMIN || which==YMAX) {
//Berechnung des Parameters t falls
//waagerechte Clippinggrenze
double square=value*y0-2*value*y1+y1*y1+value*y2-y0*y2;
if (square<0.0) {
//komplexe Loesung nicht moeglich
return null;
}
if (square==0) {
t0=(y0-y1)/(y0-2*y1+y2);
doubleT=false;
}
D Die calcQuadIntersec Methode der Klasse Polygon
117
else if(square>0.0) {
t0=(y0-y1+Math.sqrt(square))/(y0-2*y1+y2);
t1=(y0-y1-Math.sqrt(square))/(y0-2*y1+y2);
if (t1<t0) {
double hilf=t0;
t0=t1;
t1=hilf;
}
doubleT=true;
}
}
else if (which==XMIN || which==XMAX){
//Berechnung des Parameters t falls
//senkrechte Clippinggrenze
double square=value*x0-2*value*x1+x1*x1+value*x2-x0*x2;
if (square<0.0) {
//komplexe Loesung nicht moeglich
return null;
}
if (square==0) {
t0=(x0-x1)/(x0-2*x1+x2);
doubleT=false;
}
else if(square>0.0) {
t0=(x0-x1+Math.sqrt(square))/(x0-2*x1+x2);
t1=(x0-x1-Math.sqrt(square))/(x0-2*x1+x2);
if (t1<t0) {
double hilf=t0;
t0=t1;
t1=hilf;
}
doubleT=true;
}
}
if (which==YMIN) {
//obere waagerechte Clippinggrenze
Point[] newPoints = null;
//Ankerpunkte sind innerhalb und Kontrollpunkt
//ausserhalb der Clippinggrenzen,
//sowie zwei t Werte
if (y0>value && y2>=value && y1<value && doubleT) {
newPoints = new Point[3];
D Die calcQuadIntersec Methode der Klasse Polygon
118
//Punkte f"ur P0,P1’ und P2’ errechnen
//Kurve zwischen 0 und t0
//P0 bleibt erhalten
Point P1_ = LERP(P0, P1, t0);
Point P2_ = LERP(LERP(P0,P1,t0),LERP(P1,P2,t0),t0);
newPoints[0]=new Point(P2_,P1_);
//Erstes Kurvensegment errechnet
//Segment zwischen t1 und 1 errechnen
Point P0_ = LERP(LERP(P0,P1,t1), LERP(P1,P2,t1),t1);
P1_ = LERP(P1, P2, t1);
//P2 bleibt so erhalten
//gerades Liniensegment
newPoints[1] = P0_;
//zweites Kurvensegment
newPoints[2] = new Point(P2, P1_);
//fertig mit diesem Fall
}
else if(y0<value && y2>=value) {
//Die Kurve verlaeuft von aussen nach innen
Point P1_ = LERP(P1, P2, t0);
Point P0_ = LERP(LERP(P0,P1,t0),LERP(P1,P2,t0),t0);
newPoints = new Point[2];
newPoints[0] = P0_;
newPoints[1] = new Point(P2, P1_);
}
else if(y0>value && y2<=value) {
//Die Kurve verlaeuft von innen nach aussen
Point P1_ = LERP(P0, P1, t0);
Point P2_ = LERP(LERP(P0,P1,t0),LERP(P1,P2,t0),t0);
newPoints = new Point[1];
newPoints[0] = new Point(P2_, P1_);
}
else if(doubleT && (y0<value && y2<=value
&& y1>value) ) {
//Anker aussen und Kontrollpunkt innen
Point P0_ = LERP(LERP(P0,P1,t0),LERP(P1,P2,t0),t0);
Point P2_ = LERP(LERP(P0,P1,t1),LERP(P1,P2,t1),t1);
Point P1_ = LERP(P0_, LERP(P1, P2, t0), t1);
newPoints=new Point[2];
newPoints[0] = P0_;
newPoints[1] = new Point(P2_, P1_);
}
D Die calcQuadIntersec Methode der Klasse Polygon
return newPoints;
}
else if (which==XMAX) {
//rechte senkrechte Clippinggrenze
//[ ... ] analog zum Fall YMIN
}
else if (which==YMAX) {
//untere waagerechte Clippinggrenze
//[ ... ] analog zum Fall YMIN
}
else if (which==XMIN) {
//linke waagerechte Clippinggrenze
//[ ... ] analog zum Fall YMIN
}
}
119
E Inhalt der CD-Rom
E
120
Inhalt der CD-Rom
arbeit
arbeit/latex
arbeit/pakete
arbeit/pdf
arbeit/ps
arbeit/html
Diplomarbeit
Latex-Quellen und Grafiken
Tex-Pakete und Dokumentation
Acrobat Reader PDF-Version
Postscript Version
HTML-Version
beispiel
beispiel/skfdateien
beispiel/flash
beispiel/clipped
Anwendung der Flashweather-Klassen
Aus [Stark2001] erzeugte SKF-Dateien
Flashfilme
geclippte Filme
classes
classes/flashweather/generator
classes/flashweather/clipper
classes/flashweather/geomutil
classes/swf sdk
classes/f3lib
Alle Java-Klassen
Die Generator-Klassen
Die Clipper-Klassen
Die Point- und Polygon-Klasse
Das Macromedia SWF-SDK
C++ Klassen für die native-Anbindung
docs
docs/flashweather
docs/java
docs/swf
docs/w3c
docs/xerces
Dokumentation
API der Flashweather Pakete
Java 2 API
Flash Dateiformat Spezifikation
XML, DOM, SVG Spezifikationen
Xerces-J API
bibliotheken
bibliotheken/flashweather
bibliotheken/java swf api
bibliotheken/lib
Die entwickelten Bibliotheken
Die Flashweather-JAR-Dateien
JAR-Datei der Java SWF API
Die C++ Library für die Java SWF API
software
software/flash
software/java
software/swf-sdk
software/xerces
benötigte Softwarepakete
Flash Netscape-Plugin fr Linux
Java 2 SDK 1.3
Macromedia SWF-SDK
Apache Xerces-J XML-Parser
F Literaturverzeichnis
F
121
Literaturverzeichnis
Die Literaturquellen beziehen sich nicht nur auf gedruckte Literatur, sondern im wesentlichen auf Websites. Letztere bieten einen aktuelleren Zugang zu Informationen, vor allem
in der recht schnellebigen Informatik, sind aber auch gerade deshalb schnell veraltet und
werden gelöscht oder die URL ändert sich durch Umstrukturierungen auf dem Server. Zu
dem Zeitpunkt der Abgabe dieser Diplomarbeit waren alle Adressen erreichbar.
Literatur
[Apac2001b]
Apache
Apache XML Project
http://xml.apache.org
[Apac2001]
Apache
Apache Software License, Version 1.1
http://xml.apache.org/LICENSE
[Beck2000]
Becker, Oliver
Was ist XML?
http://www.informatik.hu-berlin.de/˜xing/Einstieg
[BeMi2000]
Behme, Henning ; Mintert, Stefan
XML in der Praxis, 2. erweiterte Auflage
Addison Wesley Lomgman Verlag, Pearson Education Deutschland
GmbH 2000
[Cove2001]
The XML Cover Pages
XML Onlinereference
http://xml.coverpages.org
[Deho2001]
Deuflhard, Peter; Hohmann, Andreas
Numerische Mathematik I, 2.Auflage
Walter de Gruyter, Berlin 1993
[Debo2001]
Debon, Olivier
Page for multi-platform Flash Plugin
http://www.geocities.com/TimesSquare/Labyrinth/
5084/flash/download.html
[FAQ2001]
FAQ.org
comp.graphics.algorithms FAQ
http://www.faqs.org/faqs/graphics/algorithms-faq
F Literaturverzeichnis
122
[Fell1992]
Fellner, Wolf-Dietrich
Computergrafik, 2.Auflage
Reihe Informatik, Band 58, BI-Wissenschaftsverlag, 1992
[Fine2001]
Finetuning.com
XML Parsers
http://www.finetuning.com/parse.html
[Gopr2000]
Goldfarb, Charles F.; Prescod, Paul
Das XML-Handbuch, 1.Auflage
Addison Wesley Lomgman Verlag, Pearson Education Deutschland
GmbH 2000
[Har2000]
Harold, Elliotte Rusty
Die XML Bibel, 1.Auflage
MITP-Verlag GmbH, Bonn 2000
[Har2001]
Harold, Elliotte Rusty
XML in a Nutshell, 1.Auflage
O’Reilly, Köln 2001
[IBM2001]
IBM Alphaworks
XML Parser for Java
http://www.alphaworks.ibm.com/formula/xml
[IDC2001]
International Data Corporation
Online User Forecast, Stand März 2001
http://www.idc.com
[ISO2001]
International Standard Organisation
http://www.iso.ch
[Lee2000]
Berners-Lee, Tim
Biography
http://www.w3.org/People/Berners-Lee/
[Macr2001]
Macromedia Inc.
Macromedia Flash File Format (SWF) Software Development Kit
(SDK) License Agreement
http://www.macromedia.com/software/flash/open/licensing/fileformat/
[Macr2001a]
Macromedia Inc.
Flash Player Statistics
http://www.macromedia.com/software/flash/survey/
[Macr2001b]
Macromedia Inc.
Company background
http://www.macromedia.com/macromedia/proom/
corpinfo/background.html
F Literaturverzeichnis
123
[Macr2001c]
Macromedia Inc.
Flash Player Quellen
http://www.macromedia.com/software/flash/open/
licensing/sourcecode/
[Macr2001d]
Macromedia Inc.
Flash File Format SDK Documentation
http://www.macromedia.com/software/flashplayer/downloads/
[NPDO2001]
NPD-Online
Essential Market Information
http://www.npd.com
[OpTe2001]
Open Text Corporation
Aelfred
http://www.opentext.com/microstar
[Orac2001]
Oracle
Oracle Technology Network
http://technet.oracle.com
[Oswf2001]
Open SWF.
Introduction to SWF
http://www.openswf.org/
[Scho2000]
Schoch, Frank
XML-Parser
http://www.informatik.fh-muenchen.de/˜schieder/xml-00-01/
04-parser/inhalt.html
[Schw1997]
Schwarz, Hans Rudolf
Numerische Mathematik
B. G. Teubner, Stuttgart 1997
[Stark2001]
Stark, Benjamin
FlashWeather, Vektorisierung von raum- und zeitbezogenen Daten zur
Visualisierung mit Macromedia Flash, Diplomarbeit
Universität Osnabrück 2001
[Sun2001a]
Sun Microsystems
Java Native Interface
http://java.sun.com/docs/books/tutorial/native1.1/index.html
[Sun2001b]
Sun Microsystems
Java Technology and XML
http://java.sun.com/xml
[Wol2000]
Wolter, Sascha
Flash 4, 1.Auflage
Galileo Press GmbH, Bonn 2000
F Literaturverzeichnis
[WMO1998]
World Meteorological Organzation
A guide to the code form FM 92-IX Ext. GRIB
http://www.wmo.ch/web/www/reports/Guide-binary-2.html
[W3C2001a]
World Wide Web Consortium
XML
http://www.w3c.org/XML
[W3C2001b]
World Wide Web Consortium
XHTML
http://www.w3c.org/TR/xhtml1
[W3C2001c]
World Wide Web Consortium
XML 1.0 Recommendation
http://www.w3c.org/TR/1998/REC-xml-19980210
[W3C2001d]
World Wide Web Consortium
Synchronized Multimedia
http://www.w3.org/AudioVideo/
[W3C2001e]
World Wide Web Consortium
W3C Scalable Vector Graphics (SVG)
http://www.w3.org/Graphics/SVG/Overview.htm8
124
Erklärung
Hiermit erkläre ich, daß ich die Diplomarbeit selbstständig angefertigt und keine Hilfsmittel außer denen in der Arbeit angegebenen benutzt habe.
Osnabrück, den ................
.................................
(Unterschrift)
Herunterladen