Artikel

März 2003 | Artikel

Vektorgrafiken .NET

(Link zum Artikel: http://www.it-republik.de/dotnet/artikel/0325)

SVG für .NET mit dem Open Source-Projekt SVG#

Text: von Christian Wenz
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share
SVG [1] ist ein XML-basierter W3C-Standard für Vektorgrafiken, Microsoft setzt bei .NET auch sehr stark auf XML. SVG# ist ein Open Source-Projekt, das beide Welten zueinander bringen will. Grund genug, einen genauen Blick darauf zu werfen!

Meistens, wenn das W3C ein Format als Standard (oder genauer gesagt: Recommendation, Empfehlung) verabschiedet, ist die Fachwelt zufrieden. Allerdings kann das gemeine Volk damit oftmals wenig anfangen. Bestes Beispiel ist XML - es wird seit Jahren als Allheilsbringer verkündet, aber erst jetzt wird die Verbreitung von XML tatsächlich spürbar. Davor jedoch hat Otto Normalbenutzer nur wenig von der Technologie gehabt. Bei SVG - Scalable Vector Graphics - hätte das anders sein können. Die Idee dahinter ist nämlich sehr einfach: Es gibt im World Wide Web kein weit verbreitetes Vektorgrafikformat. GIF, JPEG und PNG, die von allen Browsern unterstützt werden, sind Pixelformate, die Grafiken werden dort also in einzelnen Bildpunkten abgespeichert. Sobald eine Grafik vergrößert werden muss, gehen Details verloren, denn die zusätzlichen erforderlichen Pixel werden aufgrund des vorhandenen Bildmaterials berechnet, sind aber nicht Teil der Grafik. Vektorgrafiken verfolgen einen anderen Ansatz. Das Bild wird aus geometrischen Figuren aufgebaut: Linien, Polygonen und Kreisen. Damit eignen sich Vektorpictureer nur wenig zur Darstellung von fotorealistischen Motiven, für alles andere jedoch können die Stärken des Formats genutzt werden: Eine Skalierung (Vergrößerung und Verkleinerung) ist ohne Qualitätsverlust möglich.
SVG hat damit eine Nische besetzt. Das Format ist komplett XML-basiert und somit bereits mit einem simplen Texteditor zu erstellen. Außerdem bietet das Format eine integrierte ECMAScript-Unterstützung, sodass es möglich ist, Skriptcode in die Grafik zu integrieren und damit auch dynamische Grafiken und Animationen zu erstellen. Die Liste der Features und Vorzüge von SVG ist lang und würde wohl einen eigenen Artikel füllen.
Wieso aber ist es für SVG doch noch ein sehr langer Weg bis zur Weltherrschaft? Das ist zu einem großen Teil durch den SVG-Hauptkonkurrenten, das Macromedia-Flash-Format, begründet. Flash basiert auch auf Vektorgrafiken und kann ebenfalls durch das mittlerweile sehr stark an ECMAScript angelehnte ActionScript programmiert werden. Flash ist ein binäres, nur in Teilen offengelegtes Format, das vom Hersteller Macromedia kontrolliert wird. Diese Nachteile des Platzhirschs sind allerdings, zumindest teilweise, ideologischer Natur, ein menschenlesbares Format ist nicht per definitionem besser als ein Binärformat. Dieses Argument zieht also nicht. Die Flash-Entwicklungsumgebung von Macromedia (Macromedia Flash) ist zudem ein sehr mächtiges Tool, zu dem es für SVG-Dateien nur wenige Entsprechungen gibt.
Ein weiteres Problem ist die Verbreitung - sowohl für Flash-Filme als auch für SVG-Grafiken wird ein Plugin benötigt. Das Flash-Plugin ist bei der Standardinstallation der meisten bekannten Webbrowser mit dabei, außerdem die einzige Third-Party-Komponente in Windows XP. Laut einer im September 2002 veröffentlichen Studie [2] setzen 97 Prozent aller Websurfer irgendeine Version des Flash-Plugins ein. Der Download ist zudem unter 200 KB, also auch mit einem Modem schnell übertragen. Etwas anders sieht es da bei SVG aus. Das zurzeit am weitesten verbreitete Plugin ist der Adobe SVG Viewer [3], der in seiner aktuellen Version 3 (Version 4 ist in der Entwicklung) mit etwa 3 MB zu Buche schlägt und entgegen anders lautender Gerüchte nicht automatisch beim beliebten Adobe Acrobat Reader mit dabei ist. Aufgrund einer Änderung der Mozilla-APIs funktioniert das Plugin unter dem Open Source-Browser und auch den neueren Netscape-Versionen nur eingeschränkt. Das Projekt, in Mozilla/Netscape eine integrierte SVG-Unterstützung anzubieten, ist so gut wie eingeschlafen. Hier ist also noch ein langer Weg zu gehen. Auch das recht neue SVG-Plugin von Corel [4] funktioniert nicht mit neueren Netscape-Versionen.
Neben den Hindernissen technischer Natur und der Akzeptanz des Formats sind einige SVG-Unterstützer häufig eher hinderlich denn hilfreich. Allzu oft wird wider besseren Wissens argumentiert, werden ideologische Gräben aufgezogen und so eine unlautere Diskussion geführt. Fakt ist: SVG ist zurzeit noch ein Exot, hat aber sehr gute Zukunftschancen. Es wäre nur hilfreich, wenn die Diskussion, das Pro und Contra gegenüber Flash (auch wenn einige Unverbesserliche es nicht wahrhaben möchten: Beide Formate haben ihre eigenen Vor- und Nachteile) etwas professioneller geführt werden würde.
Trotz all dieser negativen Punkte lohnt sich die Beschäftigung mit SVG, denn allzu oft hat sich gezeigt, dass eine W3C-Empfehlung sich auch tatsächlich durchgesetzt hat. Es gibt sogar schon eine Website, die vollständig auf SVG basiert (www.svgspider.com/), auch wenn sich hier über den Nutzen sicherlich streiten ließe.

Einige SVG-Beispiele
Wie bereits erwähnt, bei einer SVG-Datei handelt es sich um ein XML-Dokument. Durch einzelne Tags werden Elemente der Grafik angegeben. Hier ein kurzes Beispiel:
  1. <?xml version="1.0"?>
  2. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
  3. "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
  4. <svg width="300" height="100">
  5. <text x="150" y="75" style="color: red; font-size:36;">
  6. dot.net magazin
  7. <animate begin="0s" dur="3s" repeatCount="indefinite"
  8. attributeName="fill" values="#ff0000; #00ff00; #0000ff; #00ff00;#ff0000;" />
  9. </text>
  10. </svg>
Im SVG-Viewer (beispielsweise dem Plugin von Adobe oder Corel) erscheint ein Schriftzug ("dot.net magazin"), der permanent seine Farbe wechselt. Der Text wird mit dem -Element dargestellt, die sich verändernde Füllung durch .
Von den grafischen Möglichkeiten einmal abgesehen, sind auch die Skriptmöglichkeiten von SVG nicht zu verachten. Die Integration von ECMAScript (der Standard für JavaScript) ermöglicht den Zugriff auf das DOM (Document Object Model) des SVG-Dokuments. Die SVG-Datei in Listing 1 beinhaltet ein noch leeres Textelement, in das mit DOM-Methoden die aktuelle Uhrzeit eingefügt wird.
Listing 1
  1. <?xml version="1.0"?>
  2. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
  3. "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
  4. <svg onload="init(evt);" width="250px" height="100px">
  5. <defs>
  6. <script type="text/ecmascript"><![CDATA[
  7. function init(evt) {
  8. svgdoc = evt.getTarget().getOwnerDocument();
  9. var uhrzeit = (new Date()).toLocaleString();
  10. var u = svgdoc.getElementById("uhr");
  11. var uz = svgdoc.createTextNode(uhrzeit);
  12. u.appendChild(uz);
  13. }
  14. ]]></script>
  15. </defs>
  16. <text id="uhr" x="13" y="50" />
  17. </svg>
Auch, wenn es sich hierbei um jeweils eher triviale Beispiele handelt, ist das Potenzial des Dateiformats schon absehbar: Es können Grafiken definiert, animiert und dynamisch verändert werden. Damit ist SVG insbesondere für den Einsatz im World Wide Web prädestiniert.
SVG mit ASP.NET erzeugen
Um SVG-Dateien mit ASP.NET zu generieren, müssen Ihre ASP.NET-Seiten, die den (dynamischen) SVG-Code generieren, den richtigen MIME-Typ zurückliefern. Fügen Sie dazu die folgende Anweisung in Ihre Seite ein:
  1. Response.ContentType = "image/svg+xml"
Wenn Sie dieses Skript aufrufen, sollten Sie für den Microsoft Internet Explorer, der sich oftmals nicht um MIME-Typen schert, dafür sorgen, dass das Skript auf .svg endet. Dies geht mit einem kleinen Trick, wenn Sie es wie folgt aufrufen:
  1. http://servername/skriptname.aspx?IE=.svg
Ihr Skript ignoriert den Querystring, der Internet Explorer geht nun garantiert davon aus, dass es sich um eine SVG-Datei handelt und zeigt diese - funktionsfähiges Plugin vorausgesetzt - an.
SVG goes .NET: SVG#
Aufgrund des - zumindest momentanen - Nischenstatus gibt es in .NET natürlich keine direkte SVG-Unterstützung, aber eine ganze Reihe von XML-Funktionalitäten. Mit etwas Aufwand ist es damit möglich, SVG nachzupictureen und somit beispielsweise einen Viewer für das Grafikformat zu ermitteln. Ihre Kunden benötigen dann keinen SVG-Viewer, Sie selbst können aber (fast) alle Vorteile des Formats nutzen und es in Ihre Applikationen einbinden. Ein entsprechendes Projekt wird zurzeit bei SourceForge.net gehostet [5]. Es ist gemeinhin als SVG# bekannt, eine Anlehnung an die verwendete Programmiersprache C#. Ein weiterer geläufiger Projektname ist SharpVectors. Das ist der Name des Projekts in Visual Studio .NET.
Nach dem Download der aktuellen Quellcodedistribution von der Projekthomepage oder dem direkten Bezug aus dem CVS-System bei SourceForge.net können Sie die Datei SharpVectors.sln durch Doppelklick in Visual Studio .NET öffnen. Dieses Projekt enthält mehrere Unterprojekte beziehungsweise Assemblies:
  • SharpVectorObjectModel - eine möglichst genaue Abbildung des SVG-DOMs. Nach und nach werden hier alle Elemente der SVG-Spezifikation implementiert, ein Großteil ist bereits vorhanden.
  • SharpVectorRenderingEngine - ein Renderer für SVG-Dateien. Hier werden SVG-Elemente in GDI-Anweisungen umgesetzt. Es wird also ein SVG-Plugin emuliert.
  • SvgComponents - diese Assembly enthält zurzeit ein selbstgestricktes Web Control, SVGPictureBox. Es handelt sich hierbei um ein Steuerelement, das SVG-Dateien anzeigen kann. Hier wird also der Renderer integriert und ein Einbau der Komponente in .NET-Anwendungen ermöglicht.
  • SharpVectorViewer - eine Demoapplikation, in der eine SVG-Datei angezeigt wird.
Werfen wir zunächst einen Blick auf eine der Dateien im Projekt SharpVectors, Dom\Svg\Shapes\SVGLineElements.cs. Dort wird das -Element implementiert, das eine Linie darstellt. Die C#-Datei enthält dazu unter anderem die folgende öffentliche Methode:
  1. public GraphicsPath GetGraphicsPath()
  2. {
  3. if(gp == null)
  4. {
  5. gp = new GraphicsPath();
  6. gp.AddLine(
  7. X1.AnimVal.Value,
  8. Y1.AnimVal.Value,
  9. X2.AnimVal.Value,
  10. Y2.AnimVal.Value);
  11. }
  12. return gp;
  13. }
Die Werte X1, Y1, X2 und Y2 sind dabei öffentliche Eigenschaften, die ebenfalls in der Klasse definiert sind. Am Ende wird ein GraphicsPath-Objekt zurückgegeben, das eine Linie mit den im SVG-Code angegebenen Start- und Endkoordinaten enthält.
Die nachfolgende Methode stammt aus dem Projekt SharpVectorRenderingEngine und liefert ein Bitmap zurück - hier wird also eine SVG-Grafik interpretiert und in Pixeldaten umgewandelt. Die Funktion selbst ist recht kurz, es wird der oberste Knoten genommen und dort die Methode Paint() aufgerufen. In dieser wird die SVG-Datei knotenweise mit GDI-Funktionen gezeichnet.
  1. public Bitmap Render()
  2. {
  3. bmp = new Bitmap(Width, Height);
  4. PaintStatus ps = new PaintStatus();
  5. Graphics gr = Graphics.FromImage(bmp);
  6. gr.Clear(ColorBack);
  7. RootGraphicsNode.Paint(gr, ps);
  8. gr.Dispose();
  9. return bmp;
  10. }
Wenn Sie das Hauptprojekt kompilieren, erhalten Sie vier Dateien: Viewer.exe, die Demoanwendung, die eine SVG-Grafik anzeigt, sowie für jede der drei Assemblies eine entsprechende DLL. Diese DLL können Sie dann in Ihre eigenen Projekte integrieren und somit auch SVG-Dateien einbinden und anzeigen. Ein kleines Beispielprojekt soll dies demonstrieren. Sie erhalten die DLLs im Übrigen auch auf der Projekthomepage als Binärdistribution.
Praxisprojekt
Erstellen Sie zunächst ein neues VB.NET-Projekt in Visual Studio .NET. Über Projekt | Verweis hinzufügen können Sie nun alle drei DLLs von SVG# hinzuzufügen. Dann ist es nämlich möglich, mittels Extras | Toolbox anpassen im Register .NET Framework-Komponenten das Benutzersteuerelement SvgPictureBox der Toolbox hinzufügen. Dann können Sie diese Komponente per Drag&Drop in Ihre Anwendung integrieren.
Die eigentliche Anwendung selbst ist simpel gestrickt. Platzieren Sie eine SvgPictureBox-Komponente (SvgPictureBox1) auf dem Formular und fügen Sie noch ein Textfeld (TextBox1) und drei Schaltflächen hinzu. Außerdem benötigen Sie noch ein OpenFileDialog-Objekt (OpenFileDialog1).
Zunächst einmal wird die erste der Schaltflächen mit Code hinterlegt. Auf Mausklick soll sich der Datei-öffnen-Dialog zeigen und der Benutzer eine SVG-Datei auswählen dürfen, deren Pfad dann in das Textfeld übertragen wird:
  1. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  2. OpenFileDialog1.CheckFileExists = True
  3. OpenFileDialog1.Filter = "SVG-Dateien (*.svg)|*.svg|Alle Dateien|*.*"
  4. OpenFileDialog1.ShowDialog(Me)
  5. End Sub
  6. Private Sub OpenFileDialog1_FileOk(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles OpenFileDialog1.FileOk
  7. TextBox1.Text = OpenFileDialog1.FileName
  8. End Sub
Die zweite Schaltfläche lädt dann die angegebene SVG-Datei und zeigt sie an. Der Code hierfür ist sehr geradlinig: Zunächst kommt eine Reihe von Abfragen, ob der angegebene Dateiname gültig ist und die Datei überhaupt existiert. Dann kommt es zur eigentlich entscheidenden Anweisung: Die Eigenschaft SourceURL des SvgPictureBox-Elements wird auf die URL der SVG-Datei gesetzt:
  1. SvgPictureBox1.SourceURL = TextBox1.Text
Die dritte Schaltfläche schließlich ruft die SVG-Datei im Microsoft Internet Explorer auf. Der Browser nämlich zeigt XML-Dateien farbkodiert an, was das Ganze sehr übersichtlich macht. Allerdings muss dazu die Dateiendung .xml vorhanden sein; bei .svg wird automatisch das SVG-Plugin gestartet. Aus diesem Grund wird ein kleiner Umweg gegangen. Zunächst wird ein temporärer Dateiname erzeugt und dieser um die Endung .xml erweitert; dann wird der Inhalt der im Textfeld angegebenen SVG-Datei in diese Datei geschrieben:
  1. Dim temp As String = Path.GetTempFileName()
  2. Dim reader As StreamReader = New StreamReader(TextBox1.Text)
  3. Dim code As String = reader.ReadToEnd
  4. reader.Close()
  5. Dim writer As StreamWriter = New StreamWriter(temp & ".xml", False)
  6. writer.Write(code)
  7. writer.Close()
Als nächstes wird einfach der Microsoft Internet Explorer gestartet und die neu erzeugte Datei darin aufgerufen. Außerdem wird der Dateiname in eine Klassenvariable geschrieben:
  1. Process.Start("iexplore.exe", "file:" & temp & ".xml")
  2. al.Add(temp)
Der Internet Explorer wird auf Mausklick geöffnet und zeigt die XML-Datei an. Allerdings wurden temporäre Dateien erzeugt. Diese sollten beim Beenden der Applikation wieder gelöscht werden:
  1. Private Sub Form1_Unload(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Closed
  2. Dim i As Integer
  3. For i = 0 To al.Count - 1
  4. File.Delete(al.Item(i))
  5. File.Delete(al.Item(i) & ".xml")
  6. Next
  7. End Sub
Den kompletten Code für die Methoden, inklusive Sicherheitsabfragen, finden Sie in Listing 2. Auf der Heft-CD finden Sie das gesamte Visual Studio .NET-Projekt sowie die erzeugten ausführbaren Dateien zum direkten Ausprobieren.
Listing 2
  1. Imports System.IO
  2. Public Class Form1
  3. Inherits System.Windows.Forms.Form
  4. #Region " Vom Windows Form Designer generierter Code "
  5. ' ...
  6. #End Region
  7. Dim al As New ArrayList()
  8. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  9. Dim fi As FileInfo
  10. Dim fn As String
  11. Try
  12. fi = New FileInfo(TextBox1.Text)
  13. fn = fi.FullName
  14. Catch
  15. MessageBox.Show("Ungültiger Dateiname!")
  16. Exit Sub
  17. End Try
  18. If Not fn Is Nothing AndAlso fi.Exists Then
  19. Try
  20. SvgPictureBox1.SourceURL = TextBox1.Text
  21. Catch ex As Exception
  22. MessageBox.Show("Fehler in der SVG-Datei!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error)
  23. End Try
  24. Else
  25. MessageBox.Show("Datei existiert nicht!")
  26. End If
  27. End Sub
  28. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  29. OpenFileDialog1.CheckFileExists = True
  30. OpenFileDialog1.Filter = "SVG-Dateien (*.svg)|*.svg|Alle Dateien|*.*"
  31. OpenFileDialog1.ShowDialog(Me)
  32. End Sub
  33. Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
  34. Dim fi As FileInfo
  35. Dim fn As String
  36. Try
  37. fi = New FileInfo(TextBox1.Text)
  38. fn = fi.FullName
  39. Catch
  40. MessageBox.Show("Ungültiger Dateiname!")
  41. Exit Sub
  42. End Try
  43. If Not fn Is Nothing AndAlso fi.Exists Then
  44. Try
  45. Dim temp As String = Path.GetTempFileName()
  46. Dim reader As StreamReader = New StreamReader(TextBox1.Text)
  47. Dim code As String = reader.ReadToEnd
  48. reader.Close()
  49. Dim writer As StreamWriter = New StreamWriter(temp & ".xml", False)
  50. writer.Write(code)
  51. writer.Close()
  52. Process.Start("iexplore.exe", "file:" & temp & ".xml")
  53. al.Add(temp)
  54. Catch ex As Exception
  55. MessageBox.Show("Fehler beim Erstellen der temporären Datei!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error)
  56. End Try
  57. Else
  58. MessageBox.Show("Datei existiert nicht!")
  59. End If
  60. End Sub
  61. Private Sub OpenFileDialog1_FileOk(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles OpenFileDialog1.FileOk
  62. TextBox1.Text = OpenFileDialog1.FileName
  63. End Sub
  64. Private Sub Form1_Unload(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Closed
  65. Dim i As Integer
  66. For i = 0 To al.Count - 1
  67. File.Delete(al.Item(i))
  68. File.Delete(al.Item(i) & ".xml")
  69. Next
  70. End Sub
  71. End Class
Wenn Sie an SVG und insbesondere SVG# Gefallen gefunden haben, verfolgen Sie weiterhin die Entwicklung des Open Source-Projekts - oder engagieren Sie sich doch selbst!
Links und Literatur


Anzeige

Kommentare

zurück zum Seitenanfang