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)