{"id":55001116,"date":"2018-02-01T00:00:00","date_gmt":"2020-05-13T21:18:05","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1116"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"HTML5_als_Grafikkomponente","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/","title":{"rendered":"HTML5 als Grafikkomponente"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Wenn es um das Erzeugen von Grafiken geht, l&auml;sst Access Sie weitgehend im Regen stehen. Maximal die Anzeige von Bilddateien &uuml;ber das Bild- oder Anlagesteuerelement ist in Formularen und Berichten vorgesehen. F&uuml;r alles dar&uuml;ber hinaus kommen Sie um den Einsatz aufw&auml;ndiger Windows-API-Funktionen nicht herum. Wir zeigen hier aber, dass Sie auch mithilfe des Webbrowser-Steuerelements und HTML5 auf einfache Weise zu verbl&uuml;ffenden Ergebnissen kommen.<\/b><\/p>\n<h2>Die HTML5- und SVG-Erweiterungen von HTML<\/h2>\n<p>Grafikelemente k&ouml;nnen auf dynamischen Webseiten &uuml;ber zwei Schnittstellen erstellt werden. Die eine ist <b>SVG<\/b> und die andere <b>HTML5<\/b>. <b>SVG<\/b> ist eine auf <b>XML<\/b> basierende Auszeichnungssprache, die beliebige grafische Objekte mitsamt Effekten erzeugen und die resultierenden Anweisungen in einer Datei mit der Endung <b>svg<\/b> abspeichern kann.<\/p>\n<p>Solche Dateien stellen Sie dann mit einem Grafikprogramm dar und bearbeiten sie dort auch, wobei sich vor allem <b>Inkscape<\/b> als Open-Source-L&ouml;sung etabliert hat. Seit geraumer Zeit beherrschen aber auch alle g&auml;ngigen Webbrowser diese Schnittstelle, wobei sie keinesfalls auf das Laden solcher Dateien angewiesen sind, sondern die Grafikelemente auch direkt &uuml;ber Objekte aufbauen k&ouml;nnen. In der Regel kommt f&uuml;r die Steuerung <b>JavaScript<\/b> zum Einsatz. <\/p>\n<p>Dasselbe gilt f&uuml;r <b>HTML5<\/b>. Auch hier gibt es vordefinierte Objekte, die Sie mit Leben f&uuml;llen. Der wesentliche Unterschied zu <b>SVG<\/b> besteht allerdings darin, dass die damit get&auml;tigten Grafikoperationen nicht mehr r&uuml;ckg&auml;ngig gemacht werden k&ouml;nnen. W&auml;hrend <b>SVG<\/b> eine Ansammlung von interaktiven (!) Grafikobjekten darstellt, die Sie sp&auml;ter einzeln im Dokument ansprechen k&ouml;nnen, zeichnet <b>HTML5<\/b> alles passiv auf eine Malfl&auml;che (<b>canvas<\/b>), wie etwa das <b>Windows-GDI<\/b> auch. Daf&uuml;r ist seine Handhabung aber auch deutlich einfacher &#8211; einer der Gr&uuml;nde, die Schnittstelle als erste zu betrachten. In einem Folgebeitrag wenden wir uns dann der alternativen <b>SVG<\/b>-Schnittstelle zu.<\/p>\n<h2>HTML5-Objektmodell<\/h2>\n<p>F&uuml;r alles, was mit HTML und dem <b>Internet Explorer<\/b> zusammenh&auml;ngt, stellt Microsoft die Bibliothek <b>MSHTML<\/b> bereit. Laden Sie diese &uuml;ber die Auswahl <b>Microsoft HTML Object Libary<\/b> in die Verweise Ihres VBA-Projekts. Im Objektkatalog erscheint sie danach unter dem Namen <b>MSHTML<\/b>.<\/p>\n<p>Diese Bibliothek ist die umfangreichste, die Sie unter Windows &uuml;berhaupt finden k&ouml;nnen. Zu jedem HTML-Element gibt es hier ein Pendant in Form einer Klasse. So entspricht ein Web-Dokument etwa der Klasse <b>HTMLDocument<\/b>, eine Tabelle dem <b>HTMLTable<\/b>, ein <b>div<\/b>-Bereich dem <b>HTMLDivElement<\/b>, und so weiter.<\/p>\n<p>Jede dieser Klassen weist genau jene Eigenschaften und Methoden auf, die das <b>W3C<\/b>-Konsortium f&uuml;r das HTML-Element definiert hat, auch wenn jeder Browser-Hersteller hier sein eigenes S&uuml;ppchen kocht und manchmal nur eine Teilmenge oder modifizierte Parameter implementiert sind. Die Angleichung ist aber in vollem Gange, so dass sich derzeit der Internet Explorer in seinem Verhalten nur noch marginal von anderen Browsern, wie Firefox oder Chrome unterscheidet.<\/p>\n<p>H&auml;ufig wird der Text eines HTML-Dokuments &uuml;ber Strings aufgebaut. Das ist mit <b>MSHTML<\/b>, mit dem Sie den Internet Explorer oder das Webbrowser-Steuerelement fernsteuern, eigentlich nicht mehr notwendig. Sie erzeugen stattdessen neue Klasseninstanzen der Elemente unter VBA und verschachteln diese im <b>HTMLDocument<\/b> oder dessen <b>HTMLBody<\/b> &uuml;ber das Setzen bestimmter Eigenschaftenparameter. Damit k&ouml;nnen Sie den HTML-Text komplett &uuml;ber Klassenobjekte erzeugen.<\/p>\n<p>Das Stichwort <b>HTML5<\/b> selbst werden Sie indessen im Objektkatalog nicht finden. Seine Implementierung versteckt sich hinter jenen Klassen, die den String <b>Canvas<\/b> enthalten, wie etwa <b>HTMLCanvasElement<\/b>, <b>CanvasGradient<\/b>, <b>CanvasPattern<\/b>, <b>ICanvasPixelArray<\/b> oder <b>CanvasRenderingContext2D<\/b>. Letztere ist von besonderer Bedeutung, weil eben sie alle ben&ouml;tigten Grafikmethoden aufweist.<\/p>\n<p>Bild 1 zeigt einen Ausschnitt der Methoden im Objektkatalog. An ihren Namen l&auml;sst sich schon ablesen, was sie bewirken sollen. <b>lineTo<\/b> etwa zeichnet eine Line vom aktuellen Ort zu den Koordinaten <b>x<\/b> und <b>y<\/b>. <b>arc<\/b> erzeugt einen Bogen. Mit den <b>Path<\/b>-Methoden legen Sie geschwungene Linienz&uuml;ge an. <b>rect<\/b> malt ein Rechteck. Die <b>Fill<\/b>-Methoden erlauben das F&uuml;llen dieser Grafikobjekte mit beliebigen Farben oder Mustern, wobei diverse Verlaufsformen m&ouml;glich sind (<b>Gradient<\/b>). Die Aufgabe besteht also darin, ein HTML-Dokument zu erzeugen, diesem ein <b>Canvas<\/b>-Element &uuml;ber eine VBA-Objektvariable zu verabreichen und anschlie&szlig;end &uuml;ber die Methoden das <b>CanvasRenderingContext2D<\/b> Zeichenoperationen darauf auszuf&uuml;hren. <b>JavaScript<\/b> entf&auml;llt. Die Ausgabe geschieht im eingebauten <b>Access-Webbrowsersteuerelement<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/Objektkatalog1.png\" alt=\"Die Zeichenoperationen der HTML5-Canvas-Klasse im Objektkatalog\" width=\"425\" height=\"580,562\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Die Zeichenoperationen der HTML5-Canvas-Klasse im Objektkatalog<\/span><\/b><\/p>\n<h2>Webbrowser-Steuerelement<\/h2>\n<p><b>HTML5<\/b> und <b>SVG<\/b> hielten mit der Version 9 des <b>Internet Explorers<\/b> Einzug und reflektierten sich auch gleich im <b>MSHTML<\/b>-Objektmodell. Das Webbrowser-Steuerelement hostet ja lediglich den Internet Explorer und kann damit praktisch alles, was der auch kann. Bis zur aktuellen Version 11 (<b>Edge<\/b> verwendet eine andere Technik und hat mit dem <b>Webbrowser Control<\/b> nichts zu tun; auch unter <b>Windows 10<\/b> nutzt das Control den Kompatibilit&auml;tsmodus des <b>IE11<\/b>) ereignete sich eine Evolution, die den Umfang der <b>W3C<\/b>-Implementierung kontinuierlich steigerte. Sie kann als weitgehend abgeschlossen betrachtet werden. <\/p>\n<p>Dumm leider, dass das Steuerelement im Urzustand nur den <b>Internet Explorer 7<\/b> hostet. Dies bedeutet, dass Microsoft aus Sicherheitsgr&uuml;nden den Internet Explorer im Steuerelement in den Zustand dieser alten Version versetzt. Und diese kennt &uuml;berhaupt noch keine <b>HTML5<\/b>&#8211; oder <b>SVG<\/b>-Elemente! In diesem Modus ignoriert das Control schlicht alle entsprechenden Anweisungen. Zum Gl&uuml;ck gibt es aber einen Workaround, den Sie schon im Beitrag der Ausgabe <b>3\/2017 GoogleMaps als Kartendienst<\/b> (<b>Shortlink 1089<\/b>) kennenlernten. Mithilfe des Moduls <b>mdlWebControl<\/b> und dem Aufruf der Prozedur <b>SetWebControlAsIE11<\/b> oder der &uuml;bergeordneten Prozedur <b>PrepareBrowser<\/b> k&ouml;nnen Sie &uuml;ber API-Funktionen erzwingen, dass der Internet Explorer im Webbrowser-Steuerelement in den Modus der Version 11 &uuml;bergeht und zus&auml;tzlich noch weitere Limitierungen aufgehoben werden. Das alles ist legitim. Dasselbe Modul findet nun auch in der Beispieldatenbank <b>HTMLImage.accdb<\/b> zum vorliegenden Beitrag Anwendung. Von einer Funktionsbeschreibung sehe ich an dieser Stelle ab, weil dazu alles ersch&ouml;pfend bereits im erw&auml;hnten Beitrag erl&auml;utert ist.<\/p>\n<p>Dergestalt vorbereitet kann das Webbrowser-Steuerelement dann auch mit <b>HTML5<\/b> und <b>SVG<\/b> umgehen.<\/p>\n<h2>Die erste HTML5-Grafik<\/h2>\n<p>Um die grundlegenden Vorg&auml;nge zu beleuchten, die die Anlage einer <b>HTML5<\/b>-Grafik im Webbrowser-Steuerelement erfordern, laden Sie das Formular <b>frmHTML5<\/b> der Beispieldatenbank. Zun&auml;chst sehen Sie hier nur einen augenscheinlich leeren Detailbereich, sowie einen Button rechts oben im Formularkopf. Nach dessen Bet&auml;tigung kommt es zur Ausgabe der ziemlich sinnfreien Grafik in Bild 2. Aber immerhin demonstriert das Beispiel einige Grafikelemente, die andernorts vielleicht n&uuml;tzlicher verwandt werden k&ouml;nnten.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/frmHTML5.png\" alt=\"Diese Grafik im Formular frmHTML5 wird mit nur recht wenigen Zeilen VBA-Code erzeugt\" width=\"650\" height=\"523,223\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Diese Grafik im Formular frmHTML5 wird mit nur recht wenigen Zeilen VBA-Code erzeugt<\/span><\/b><\/p>\n<p>Im Entwurf des Formulars finden Sie das Webbrowser-Steuerelement mit dem Namen <b>ctlWeb<\/b> im Detailbereich. Es f&uuml;llt diesen fast ganz aus und hat die Einstellung <b>Nach unten und quer dehnen<\/b> f&uuml;r die Eigenschaft <b>Verankern<\/b>. Damit macht das Steuerelement alle Gr&ouml;&szlig;en&auml;nderungen des Formulars mit. Der Steuerelementinhalt ist an kein Tabellenfeld gebunden, sondern auf den festen Wert <b>=&#8220;about:blank&#8220;<\/b>. Ohne diese Einstellung f&auml;nden Sie n&auml;mlich nach dem Laden des Formulars im Webbrowser die unsch&ouml;ne Ausgabe <b>Die Adresse ist ung&uuml;ltig vor<\/b>. <b>about:blank<\/b> hingegen rendert gleich zu Beginn eine leere Fl&auml;che und legt ein rudiment&auml;res Dokument an, mit dem Sie anschlie&szlig;end weiterarbeiten k&ouml;nnen, um ihm die HTML5-Elemente unterzujubeln. Schauen wir uns dazu einmal den Code der Formularklasse an. Beim Laden des Formulars wird zun&auml;chst diese Zeile abgesetzt:<\/p>\n<pre>Me!ctlWeb.Object.Silent = <span style=\"color:blue;\">True<\/span><\/pre>\n<p>Aufgepasst! Das Webbrowser-Steuerelement schleift keineswegs alle Methoden des zugrundeliegenden <b>Internet Explorer-<\/b>Objekts durch. Deshalb muss dessen Instanz erst &uuml;ber die Eigenschaft <b>Object<\/b> des Steuerelements erhalten werden, &auml;hnlich, wie auch bei ActiveX-Steuerelementen &uuml;blich. Und eine seiner Eigenschaften ist <b>Silent<\/b>, die hier auf <b>True<\/b> eingestellt wird. Das weist ihn an, jegliche Meldungen zu fehlerhaften Skripten oder &auml;hnliches zu unterlassen. Das hat zwar direkt nichts mit unserem HTML5-Vorhaben zu tun, ist jedoch grunds&auml;tzlich sinnvoll, wenn Sie dem Anwender Ihrer Datenbank im Zweifel nicht die Herkunft des dargestellten Inhalts verraten m&ouml;chten &#8230;<\/p>\n<p>Der Klick auf den Button <b>cmdCanvas<\/b> ruft nun die Ereignisprozedur in Listing 1 auf. Hier wird zun&auml;chst die Objektvariable <b>oDoc<\/b> vom Typ <b>HTMLDocument<\/b> (<b>MSHTML<\/b>-Bibliothek) auf das im Webbrowser-Steuerelement befindliche leere Dokument gesetzt. Der Hintergrund wird dann &uuml;ber das <b>style<\/b>-Attribut <b>bgColor<\/b> des <b>body<\/b>-Elements auf hellgrau (<b>lightgray<\/b>) eingestellt. In der Abbildung wird das durch die leicht graue Umrandung der Grafik deutlich. Das Setzen solcher Attribute wirkt sich &uuml;brigens immer unmittelbar im Browser aus. Bis hierhin hat das Ganze noch nichts mit <b>HTML5<\/b> zu tun, was sich jedoch in der folgenden Zeile &auml;ndert. Die erzeugt &uuml;ber die Methode <b>createElement<\/b> des Dokuments ein allgemeines <b>HTMLElement<\/b> vom Typ <b>canvas<\/b>. Unterst&uuml;tzt der Browser <b>HTML5<\/b> nicht, etwa, weil sein Modus noch unver&auml;ndert auf der Version 7 des Internet Explorers beruht, so ereignet sich hier kein Fehler, sondern die Objektvariable <b>oElement<\/b> hat schlicht den Wert <b>Nothing<\/b> oder aber sie hat keinen spezifischen Typ. Deshalb fragt die n&auml;chste Zeile &uuml;ber die Hilfsroutine <b>IsCanvasSupported<\/b> noch ab, ob das <b>Canvas<\/b>-Element erfolgreich angelegt wurde und verl&auml;sst andernfalls die Routine.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdCanvas_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>oDoc<span style=\"color:blue;\"> As <\/span>HTMLDocument\r\n     <span style=\"color:blue;\">Dim <\/span>oElement<span style=\"color:blue;\"> As <\/span>IHTMLElement\r\n     <span style=\"color:blue;\">Dim <\/span>oCanv<span style=\"color:blue;\"> As <\/span>IHTMLCanvasElement\r\n     <span style=\"color:blue;\">Dim <\/span>CTX<span style=\"color:blue;\"> As <\/span>ICanvasRenderingContext2D\r\n     <span style=\"color:blue;\">Dim <\/span>oGrad<span style=\"color:blue;\"> As <\/span>ICanvasGradient\r\n     <span style=\"color:blue;\">Set<\/span> oDoc = Me!ctlWeb.Object.Document\r\n     oDoc.body.setAttribute \"bgColor\", \"lightgray\"\r\n     <span style=\"color:blue;\">Set<\/span> oElement = oDoc.createElement(\"canvas\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsCanvasSupported(oElement)<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Sub<\/span>\r\n     oElement.Id = \"zeichnung\"\r\n     <span style=\"color:blue;\">Set<\/span> oCanv = oElement\r\n     oCanv.Width = 600\r\n     oCanv.Height = 400\r\n     oDoc.body.appendChild oElement\r\n     <span style=\"color:blue;\">Set<\/span> CTX = oCanv.getContext(\"2d\")\r\n     <span style=\"color:blue;\">With<\/span> CTX\r\n         <span style=\"color:blue;\">Set<\/span> oGrad = .createLinearGradient(0, 0, 500, 0)\r\n         oGrad.addColorStop 0, \"#e0e0e0\"\r\n         oGrad.addColorStop 0.5, \"#90b080\"\r\n         oGrad.addColorStop 1, \"#808080\"\r\n         .FillStyle = oGrad\r\n         .fillRect 10, 10, 500, 300\r\n         .RECT 30, 320, 100, 30\r\n         .Fill\r\n         .stroke\r\n         .strokeRect 40, 350, 100, 30\r\n         .globalAlpha = 0.2\r\n         .moveTo 20, 200          ''''180, 200\r\n         .lineWidth = 7\r\n         .arc 100, 200, 80, 0, 6.4, 0\r\n         .stroke\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Debug.Print<\/span> oDoc.all(0).outerHTML\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Komplette Routine zur Anlage der HTML5-Grafik<\/span><\/b><\/p>\n<p>Dem <b>Canvas<\/b>-Element wird nun die eindeutige <b>Id<\/b> <b>zeichnung<\/b> verabreicht, damit sp&auml;ter gegebenenfalls auf es Bezug genommen werden kann. Da die Variable <b>oElement<\/b> nur vom allgemeinen Typ <b>HTMLElement<\/b> ist, unterst&uuml;tzt Intellisense von VBA auf sie nicht die speziellen Methoden des <b>Canvas<\/b>-Elements. Deshalb kommt die Hilfsvariable <b>oCanv<\/b> ins Spiel, die dezidiert den Typ <b>IHTMLCanvasElement<\/b> aufweist. Sie wird direkt auf die Variable <b>oElement<\/b> gecastet. Kleiner Einschub zur Hilfsroutine <b>IsCanvasSupported<\/b>: Die Funktion ermittelt einfach den Typ des in der Variablen <b>oElement<\/b> gespeicherten HTML-Elements:<\/p>\n<pre><span style=\"color:blue;\">If <\/span>TypeName(oElement) &lt;&gt; \"HTMLCanvasElement\"<span style=\"color:blue;\"> Then<\/span>\r\n...(IsCanvasSupported = <span style=\"color:blue;\">False<\/span>)...  Else = <span style=\"color:blue;\">True<\/span> ...<\/pre>\n<p>Nun haben wir zwar ein Element vom Typ <b>Canvas<\/b> angelegt, doch dieses ist damit noch lange nicht Bestandteil des Dokuments, sondern existiert rein im Speicher. Die Methode <b>appendChild<\/b> des <b>Body<\/b>-Objekts erledigt dies in der Folge aber.<\/p>\n<p>Das <b>Canvas<\/b>-Element selbst kennt noch keinerlei Methoden zum Zeichnen. Nur der sogenannte <b>Render-Kontext<\/b> kann das. Sie erhalten diesen &uuml;ber den Methodenausdruck <b>getContext(&#8222;2D&#8220;)<\/b>. Das resultierende Objekt ist vom Typ <b>ICanvasRenderingContext2D<\/b> und wird in der Objektvariablen <b>CTX<\/b> abgespeichert. Der Parameter <b>2D<\/b> ist &uuml;brigens der einzige, der hier aktuell erlaubt ist. Definiert ist &uuml;ber <b>W3C<\/b> zwar auch der Parameter <b>3D<\/b> f&uuml;r dreidimensionale Grafiken, doch der Internet Explorer unterst&uuml;tzt das, wie viele andere Browser auch, noch gar nicht. <\/p>\n<p>Die Vorarbeiten sind damit abgeschlossen und es kann nun direkt an die Zeichenoperationen gehen. Da derer mehrere folgen, ist die Variable <b>CTX<\/b> mit einem <b>With<\/b>-Block versehen, der fast bis ans Ende der Prozedur reicht.<\/p>\n<p>Wir picken uns aus der Reihe der Anweisungen die Methode <b>fillRect<\/b> heraus. Die erzeugt ein gef&uuml;lltes Rechteck. Als Parameter erwartet sie die Koordinaten der linken oberen Ecke, sowie Breite und H&ouml;he des Rechtecks. Allein ausgef&uuml;hrt resultierte sie in einem komplett schwarzen Rechteck. Denn Angaben etwa zur Linienst&auml;rke der Umrandung und zur Art und Farbe der F&uuml;llung werden der Methode ja nicht &uuml;bergeben. Die Methode greift n&auml;mlich auf den Kontext des <b>Canvas<\/b> zur&uuml;ck, in dem bereits einige Voreinstellungen zu diesen Stilen festgelegt sind. Um diese Stileinstellungen zu &auml;ndern, bedienen Sie sich weiterer Methoden des Objekts, und das &uuml;bernehmen die Zeilen vor dem Aufruf von <b>fillRect<\/b>.<\/p>\n<p>Soll ein Grafikelement nicht nur einfarbig gef&uuml;llt werden, sondern mit einem Verlauf, so muss die Eigenschaft <b>FillStyle<\/b> des Grafikkontexts auf ein Verlaufsobjekt gesetzt werden. Dieses Objekt vom Typ <b>ICanvasGradient<\/b>, zugewiesen der Variablen <b>oGrad<\/b>, erzeugt die Prozedur &uuml;ber die Methode <b>createLinearGradient<\/b>. Sie erwartet als Parameter zwei Punktkoordinaten in der Einheit <b>Pixel<\/b>, die der gew&uuml;nschten Ausdehnung des Verlaufs entsprechen. Hier ist das einerseits die linke obere Ecke (<b>0,0,<\/b>) und dann die rechte obere (<b>500,0<\/b>). Der Verlauf erstreckt sich damit von links bis fast nach rechts. Sie k&ouml;nnen hier &uuml;ber andere Werte nat&uuml;rlich auch einen diagonalen Verlauf erzeugen, der problemlos auch &uuml;ber den Rand des Canvas hinausgehen darf. Zu erw&auml;hnen w&auml;re an dieser Stelle auch, dass die alternative Methode <b>createRadialGradient<\/b> einen kreisf&ouml;rmigen Verlauf erzeugen k&ouml;nnte.<\/p>\n<p>Anschlie&szlig;end m&uuml;ssen die Farben f&uuml;r den Verlauf bestimmt werden, was die Methode <b>addColorStop<\/b> erm&ouml;glicht. Sie erwartet zwei Parameter: Der erste gibt dabei den Abstand innerhalb des Verlaufs an, auf den die Farbe wirken soll. Der Wertebereich des <b>Single<\/b>-Werts erstreckt sich von <b>0<\/b> bis <b>1<\/b>. <b>0<\/b> bedeutet in unserem Beispiel ganz links, <b>1<\/b> w&auml;re ganz rechts, also bei der Breite <b>500<\/b> Pixel. Mit <b>0,5 <\/b>treffen Sie genau die Mitte der Fl&auml;che, die sich bei der Breite <b>250<\/b> Pixel befindet. Es k&ouml;nnen beliebig viele solcher Farbbereiche durch Wiederholung der Methode <b>addColorStop<\/b> angelegt werden. Wenn Sie wollen, also ein ganzes buntes Farbspektrum. Die Farben selbst geben Sie jeweils im zweiten Parameter als hexadezimalen String an, wie sonst auch f&uuml;r solche HTML-Farbattribute &uuml;blich. Unsere drei Farbbereiche sind auf hellgrau, gr&uuml;n und dunkelgrau gesetzt. Die Methode <b>fillRect<\/b> f&uuml;llt das Rechteck nun mit diesem im Kontext neu bestimmten Verlauf. Es nimmt fast den gesamten Raum des <b>Canvas<\/b> ein.<\/p>\n<p>Es gibt noch eine alternative Methode, ein gef&uuml;lltes Rechteck zu erzeugen. Das machen die n&auml;chsten drei Zeilen deutlich. Die Methode <b>RECT<\/b> legt zun&auml;chst ein Rechteck auf dem Hintergrund an, welches ohne F&uuml;llung daherkommt. Ein einfacher Aufruf von <b>fill<\/b> &auml;ndert dies. Sie f&uuml;llt die zuletzt angelegten Grafikobjekte mit der im Kontext weiter oben festgelegten Verlaufseigenschaft. Zu sehen bekommen Sie das tats&auml;chlich jedoch erst, nachdem Sie die Methode <b>stroke<\/b> aufrufen. Sie erst rendert alle zuvor angelegten Grafikobjekte in den <b>Canvas<\/b>. Im Klartext hei&szlig;t das, dass sie mehrere Grafikobjekte, etwa auch Kreise (<b>arc<\/b>), hintereinander erzeugen k&ouml;nnen, sie auf einen Schlag mit <b>fill<\/b> f&uuml;llen und dann ebenso komplett &uuml;ber <b>stroke<\/b> physisch anzeigen k&ouml;nnen. In der Abbildung sehen Sie das verlaufsgef&uuml;llte kleine Rechteck links unten.<\/p>\n<p>Darunter befindet sich noch ein ungef&uuml;lltes. Das k&ouml;nnte ebenfalls &uuml;ber die Methode <b>RECT<\/b>, gefolgt von <b>stroke<\/b>, erzeugt werden. Einfacher aber ist die kombinierende Methode <b>stokeRect<\/b>, welche das auf einen Schlag erledigt.<\/p>\n<p>Nun fehlt noch der Kreis in der Abbildung, der &uuml;ber die Methode <b>arc<\/b> erzeugt wird, welche auch Teilbogen darstellen kann. Die Reihenfolge der Parameter: Erst die <b>x<\/b>&#8211; und <b>y<\/b>-Koordinaten des Mittelpunkts (<b>100, 200<\/b>), dann der Radius &#8211; alle Angaben in Pixel. Darauf folgen Start- und Endwinkel im Bogenma&szlig; (<b>0, 6.4<\/b>). <b>6.4<\/b> entspricht ungef&auml;hr zweimal <b>Pi<\/b>, weshalb ein kompletter Kreis gemalt wird. Der Winkel <b>0<\/b> befindet sich &uuml;brigens ganz rechts vom Mittelpunkt und die Zeichenrichtung geschieht im Uhrzeigersinn, wenn der allerletzte Parameter der Methode auf <b>0<\/b> steht. Mit dem Wert <b>1<\/b> w&uuml;rde gegen den Uhrzeigersinn gezeichnet.<\/p>\n<p>Sie k&ouml;nnen mit dieser Methode also beliebige Kreisabschnitte erzeugen, nicht jedoch ellipsenf&ouml;rmige. Die Breite des Zeichenstifts wurde zuvor &uuml;ber die Eigenschaft <b>lineWidth<\/b> des Kontexts auf 7 Pixel eingestellt. Wo aber kommt die Querlinie her, die den Kreis zu einem Stoppschild macht Eine weitere Methode zum Linienzeichnen findet sich ja in der Routine nicht.<\/p>\n<p>Grund daf&uuml;r ist die Tatsache, dass dich der Canvas den Endpunkt der letzten Zeichenoperation hernimmt und ihn als Startpunkt der n&auml;chsten verwendet. Und die letzte Aktion war das Rechteck ganz unten. Der Startpunkt des Kreises beg&auml;nne damit bei dessen Ecke links oben. Genauer: Es w&uuml;rde zus&auml;tzlich eine gerade Linie von dieser Ecke zum Kreisanfang gezeichnet werden. Auf diese Weise kommt es durch mehrere aufeinanderfolgende Grafikoperationen zu einem Linienzug. Der kann aber durch die Methode <b>moveTo<\/b> unterbrochen werden. In der Prozedur wird der Stift gleichsam zur Koordinate 80 Pixel links vom Mittelpunkt des Kreises bewegt (<b>x=100, r=80, r-x = 20<\/b>). Von hier aus entsteht dadurch eine waagrechte Linie zum Startpunkt des Kreises rechts. Die ganze Geschichte wirkt sich auch hier erst dann sichtbar aus, nachdem die Methode <b>stroke<\/b> aufgerufen wird.<\/p>\n<p>Eine weitere <b>Style<\/b>-Eigenschaft des <b>Canvas<\/b> ist au&szlig;erdem <b>globalAlpha<\/b>. Die entspricht unter HTML sonst dem Attribut <b>opacity<\/b> und gibt den Grad der Durchsichtigkeit der folgenden Grafikoperationen an. Der Kreis ist deshalb durchscheinend. Dieses Feature, den Alpha-Kanal zu beeinflussen, erm&ouml;glicht unter HTML5 &uuml;berlagernde Grafikelemente! <b>globalAlpha<\/b> hat allerdings einen etwas eigent&uuml;mlichen Wertebereich. <b>1<\/b> bedeutet opak, <b>0<\/b> v&ouml;llig durchsichtig. Doch <b>0.5 <\/b>bedeutet noch keinesfalls halbdurchsichtig! Dies kommt eher durch einen Wert von <b>0.3<\/b> zustande. Selbst beim Wert <b>0.01<\/b> ist der Kreis noch deutlich zu erkennen. Offenbar verwendet <b>HTML5<\/b> hier eine exponentielle Kurve.<\/p>\n<p>Sie sehen, dass sich &uuml;ber <b>HTML5<\/b> mit nur wenigen Zeilen Code Grafikelemente erzeugen lassen, die mit dem <b>Windows-API <\/b>(<b>GDI32<\/b> oder <b>GDIPlus<\/b>) hunderte Zeilen erfordert h&auml;tten! An dieser Stelle kann nicht jede Methode des <b>Canvas<\/b> und deren Parameter erl&auml;utert werden. Die Referenz dazu finden Sie in der <b>MSDN<\/b> von Microsoft (<b>https:\/\/msdn.microsoft.com\/de-de\/library\/ff976025(v=vs.85).aspx<\/b>). Hier ging es zun&auml;chst nur um die grundliegenden Schritte, die zu einer <b>HTML5<\/b>-Grafik im Webbrowser-Steuerelement f&uuml;hren. Die letzte Zeile der Routine gibt den erzeugten Quelltext des Dokuments &uuml;ber die Methode <b>outerHTML<\/b> im VBA-Direktfenster aus. Und der ist reichlich &uuml;bersichtlich:<\/p>\n<pre>&lt;html&gt;\r\n   &lt;head&gt;&lt;\/head&gt;\r\n   &lt;body bgcolor=\"lightgray\"&gt;\r\n     &lt;canvas width=\"600\" height=\"400\" id=\"zeichnung\"&gt;&lt;\/canvas&gt;\r\n   &lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>Hier wird offenbar, dass die Zeichenoperationen sich, anders, als bei <b>SVG<\/b>, in keiner Weise im Quelltext wiederspiegeln. Sie lassen sich durch erneutes Laden des Quelltexts nicht mehr reproduzieren. Die erstellte Grafik ist ein reines Bitmap im Browser. Wie Sie an die Daten dieses Bitmaps herankommen, um sie in einer Datei oder direkt in Tabellenfeldern abzuspeichern, folgt sp&auml;ter. Neben Grafikelementen kann <b>HTML5<\/b> nat&uuml;rlich auch Texte ausgeben. Beides kombiniert er&ouml;ffnen sich weite Anwendungsbereiche. Denken Sie an Flussdiagramme, technische Zeichnungen oder auch spezielle Grids, die sich aus den Tabellen der Datenbank speisen. Der einzige Nachteil von <b>HTML5<\/b> ist die fehlende Interaktivit&auml;t. Sie k&ouml;nnen etwa nicht auf ein Grafikelement klicken und es &uuml;ber ein Ereignis identifizieren. Daf&uuml;r brauchen Sie die in einem Folgebeitrag dargestellte Alternative <b>SVG<\/b>.<\/p>\n<h2>Diagramme per HTML5 ausgeben<\/h2>\n<p>Das Grundwissen ist nun vermittelt und wir schreiten zu einem n&uuml;tzlicheren Beispiel, dem Anlegen von Balken- und Tortendiagrammen &uuml;ber <b>HTML5<\/b>. Implementiert ist das im Formular <b>frmHTML5Diagram<\/b> der Beispieldatenbank (s. Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/frmDgmChart2.png\" alt=\"Das HTML5-Balkendiagramm zu Ums&auml;tzen nach Monaten im Formular frmHTML5Diagram\" width=\"650\" height=\"458,1014\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Das HTML5-Balkendiagramm zu Ums&auml;tzen nach Monaten im Formular frmHTML5Diagram<\/span><\/b><\/p>\n<p>Dessen Funktionen sind schnell beschrieben. Nach dem &ouml;ffnen finden Sie im Detailbereich zun&auml;chst eine wei&szlig;e Fl&auml;che vor, bei der es sich tats&auml;chlich um ein Webbrowser-Steuerelement handelt. Im Formularkopf sind einige Controls untergebracht, die die Anlage der Diagramme steuern. Sie k&ouml;nnen zum einen w&auml;hlen, ob ein Balken- oder ein Tortendiagramm generiert werden soll. Zum anderen ermitteln Abfragen auf eine Kunden- und Bestelldatenbank die aggregierten Ums&auml;tze nach Monaten oder die Ums&auml;tze nach Herkunftsl&auml;ndern der Besteller.<\/p>\n<p>Je nach Auswahl wird dann ein anderes Diagramm &uuml;ber den Button links oben erzeugt. Das fertige Diagramm k&ouml;nnen Sie &uuml;ber die Schaltfl&auml;chen rechts als Grafikdatei abspeichern oder auch direkt ausdrucken.<\/p>\n<p>Das zugeh&ouml;rige an die bekannte <b>Nordwind<\/b>-Datenbank angelehnte Datenmodell zeigt Bild 4. Es gibt Kunden (<b>tblKunden<\/b>), die Artikel (<b>tblArtikel<\/b>) bestellen. Die Verkn&uuml;pfung geschieht &uuml;ber die <b>1:n-Tabelle tblBestellungen<\/b>. Jeder Kunde kann ja mehrmals bestellen. Jede Bestellung wiederum kann mehrere Artikel enthalten, weshalb hier zus&auml;tzlich die <b>n:m-Tabelle tblBestellDetails<\/b> ben&ouml;tigt wird. <\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/rellayout_kunden.png\" alt=\"Am Datenmodell zu Kunden, Bestellungen und Artikeln sind vier relationale Tabellen beteiligt\" width=\"650\" height=\"241,0715\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Am Datenmodell zu Kunden, Bestellungen und Artikeln sind vier relationale Tabellen beteiligt<\/span><\/b><\/p>\n<p>Zur Auswertung der Ums&auml;tze pro Zeiteinheit sind nach diesem Modell alle drei rechten Tabellen zu befragen, denn jene ergeben sich aus der Summe des Felds <b>Einzelpreis<\/b> in der Artikeltabelle. Das ist warenwirtschaftlich zwar nicht korrekt, weil im richtigen Leben der Bestellpreis eigentlich zus&auml;tzlich in der Tabelle <b>tblBestelldetails<\/b> stehen m&uuml;sste &#8211; beim Versand k&ouml;nnte sich der Artikelpreis ja bereits ge&auml;ndert haben und die Rechnung enthielte dann einen falschen Betrag. Dieses uralte Modell kam so aber bereits in verschiedenen Beispieldatenbanken von <b>Access im Unternehmen<\/b> zum Einsatz, weshalb wir es hier einfach unver&auml;ndert &uuml;bernehmen.<\/p>\n<p><!--30percent--><\/p>\n<p>Wie auch immer, die zur Umsatzauswertung geh&ouml;rige Abfrage stellt Bild 5 dar. Gruppiert wird hier nach dem <b>Bestelldatum<\/b>, wobei die <b>Format<\/b>-Funktion Jahre und Monate zusammenfasst. F&uuml;r den <b>24.11.2015 <\/b>etwa<b> <\/b>erg&auml;be sich damit der Textausdruck <b>2015\/11<\/b>. Der Umsatz ergibt sich aus der Summe der <b>Einzelpreise<\/b> multipliziert mit der <b>Artikelanzahl<\/b> in <b>tblBestellungen<\/b>. Der <b>Rabatt<\/b> wird der Einfachheit halber ignoriert. Das Ergebnis der Abfrage ist eine nach Jahren und Monaten aufsteigend sortierte Liste der Ums&auml;tze und damit die Basis f&uuml;r das Diagramm weiter oben.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/qryUmsatzMonat.png\" alt=\"Zur Auswertung der Ums&auml;tze nach Monaten f&uuml;r die Diagramme kommt die Gruppierungsabfrage qry_UmsatzMonat zum Einsatz\" width=\"700\" height=\"238,832\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Zur Auswertung der Ums&auml;tze nach Monaten f&uuml;r die Diagramme kommt die Gruppierungsabfrage qry_UmsatzMonat zum Einsatz<\/span><\/b><\/p>\n<p>Sollen die Ums&auml;tze nach Herkunftsl&auml;ndern der Besteller ausgewertet werden, sind hingegen alle vier Tabellen in die Auswahlabfrage zu integrieren, da das <b>Land<\/b> ein Attributfeld des Kunden ist. Es ergibt sich die Abfrage aus Bild 6, in der nach dem Feld <b>Land<\/b> des Kunden gruppiert wird und sich die Umsatzsumme <b>S<\/b> auf gleiche Weise errechnet, wie bei der zuvor er&ouml;rterten Abfrage.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/qryUmsatzLand.png\" alt=\"Zur Auswertung der Ums&auml;tze nach Land f&uuml;r die Diagramme kommt die Gruppierungsabfrage qry_UmsatzLaender zum Einsatz\" width=\"650\" height=\"318,9591\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Zur Auswertung der Ums&auml;tze nach Land f&uuml;r die Diagramme kommt die Gruppierungsabfrage qry_UmsatzLaender zum Einsatz<\/span><\/b><\/p>\n<p>Die Sortierung erfolgt hier aber nach der Umsatzsumme absteigend, wodurch dann das Land mit dem h&ouml;chsten Umsatz ganz oben steht. Das reflektiert sich dann auch im alternativ erzeugten Diagramm in Bild 7, das zudem eine andere Farbgebung aufweist. Beide Diagramme werden aber &uuml;ber den gleichen VBA-Code generiert, der in ein Klassenmodul <b>clsHTMLDiagram<\/b> ausgelagert ist. &uuml;ber Eigenschaften der Klasse und Parameter der Methodenaufrufe k&ouml;nnen Sie dann die Gestalt des Diagramms beeinflussen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/frmDgmChart1.png\" alt=\"&auml;hnlich das HTML5-Balkendiagramm zu Ums&auml;tzen nach L&auml;ndern im Formular frmHTML5Diagram\" width=\"650\" height=\"458,1014\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: &auml;hnlich das HTML5-Balkendiagramm zu Ums&auml;tzen nach L&auml;ndern im Formular frmHTML5Diagram<\/span><\/b><\/p>\n<p>Tats&auml;chlich finden Sie im Formularcode nichts, was mit <b>HTML5<\/b> zu tun h&auml;tte, weil all dies im eingeklinkten Klassenmodul passiert:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>CDgm<span style=\"color:blue;\"> As <\/span>clsHTMLDiagram    ''''Modulkopf\r\n<span style=\"color:blue;\">Private <\/span>CDgmPie<span style=\"color:blue;\"> As <\/span>clsHTMLPieChart\r\n<span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Set<\/span> CDgm = <span style=\"color:blue;\">New<\/span> clsHTMLDiagram\r\n     <span style=\"color:blue;\">Set<\/span> CDgmPie = <span style=\"color:blue;\">New<\/span> clsHTMLPieChart\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Objektvariable <b>CDgm<\/b> wird formularglobal deklariert und beim Laden des Formulars erzeugt. Dasselbe geschieht mit der separaten f&uuml;r das Tortendiagramm verantwortlichen Klasse <b>clsHTMLPieChart<\/b>, die in der Variablen <b>CDgmPie<\/b> landet. Klicken Sie auf den Button <b>cmdDiagram<\/b> mit der Beschriftung <b>Erzeuge Diagramm<\/b>, so st&ouml;&szlig;t dies die Ereignisprozedur aus Listing 2 an, die schlie&szlig;lich die Diagramme generiert.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdDiagram_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>rs<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>sTitle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bRandom<span style=\"color:blue;\"> As Boolean<\/span>\r\n     NewDoc\r\n     Select Case optGrp.Value\r\n     <span style=\"color:blue;\">Case <\/span>1\r\n         <span style=\"color:blue;\">Set<\/span> rs = CurrentDb.OpenRecordset(\"qry_UmsaetzeMonat\")\r\n         sTitle = \"Ums&auml;tze nach Monaten\"\r\n         bRandom = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">Case <\/span>2\r\n         <span style=\"color:blue;\">Set<\/span> rs = CurrentDb.OpenRecordset(\"qry_UmsatzLaender\")\r\n         sTitle = \"Gesamtumsatz nach L&auml;ndern\"\r\n         bRandom = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">Case Else<\/span>\r\n         <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">If <\/span>optgrp2.Value = 1<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">With<\/span> CDgm\r\n             .Width = Me!ctlWeb.Object.Width\r\n             .Height = Me!ctlWeb.Object.Height\r\n             .BackColor = <span style=\"color:blue;\">RGB<\/span>(224, 224, 224)\r\n             .Title = sTitle\r\n             .RandomColors = bRandom\r\n             <span style=\"color:blue;\">Set<\/span> .DataRecordset = rs\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> .CreateDiagramDoc(Me!ctlWeb.Object)<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> \"Erzeugen des Diagramms schlug fehl\"\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">With<\/span> CDgmPie\r\n             .Width = Me!ctlWeb.Object.Width\r\n             .Height = Me!ctlWeb.Object.Height\r\n             .BackColor = <span style=\"color:blue;\">RGB<\/span>(224, 224, 224)\r\n             .Title = sTitle\r\n             .RandomColors = bRandom\r\n             <span style=\"color:blue;\">Set<\/span> .DataRecordset = rs\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> .CreateDiagramDoc(Me!ctlWeb.Object)<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> \"Erzeugen des Diagramms schlug fehl\"\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     rs.Close\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die Formularroutine zum Erzeugen des Diagramms<\/span><\/b><\/p>\n<p>In dieser erfolgt zun&auml;chst ein Aufruf der Hilfsprozedur <b>NewDoc<\/b>, welche im Webbrowser-Steuerelement <b>ctlWeb<\/b> per <b>about:blank<\/b> ein neues leeres Dokument anlegt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>NewDoc()\r\n     ctlWeb.Object.Navigate2 \"about:blank\"\r\n     Do\r\n         DoEvents\r\n     <span style=\"color:blue;\">Loop<\/span> Until ctlWeb.Object.ReadyState &gt; 2\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Im <b>Select-Case<\/b>-Zweig entscheidet die Routine anhand des in der Optionsgruppe <b>optGrp<\/b> eingestellten Werts, ob die Ums&auml;tze nach Monaten oder nach L&auml;ndern ausgegeben werden sollen. Von diesem Wert h&auml;ngt ab, welche der beiden Abfragen in das Recordset <b>rs<\/b> geladen wird. Zus&auml;tzlichen werden der Titel des Diagramms (<b>sTitle<\/b>) und die zuf&auml;llige Farbgebung (<b>bRandom<\/b>) gesetzt. Der n&auml;chste Bedingungszweig auf die Optionsgruppe <b>optGrp2<\/b> wertet aus, welche Diagrammart generiert werden soll. Im einen Fall werden Eigenschaftswerte f&uuml;r die Diagrammklasse <b>CDgm<\/b> eingestellt, im anderen die f&uuml;r die Klasse <b>CDgmPie<\/b>. Die Methoden der Klassen sind dabei aber identisch.<\/p>\n<p>Sie teilen den Klassen mit, wie breit und hoch das Diagramm werden soll (<b>Width<\/b>, <b>Height<\/b>). Die Eigenschaft <b>BackColor<\/b> stellt dessen Hintergrundfarbe ein, <b>Title<\/b> den Titeltext links oben im Diagramm. Ob zuf&auml;llige Farben f&uuml;r die Balken oder Tortenst&uuml;cke erscheinen sollen, entscheidet <b>RandomColors<\/b> &uuml;ber einen <b>Boole-Wert<\/b>. Zuletzt ist lediglich das im ersten Schritt ge&ouml;ffnete Recordset <b>rs<\/b> &uuml;ber die Methode <b>DataRecordset<\/b> zuzuweisen. Und nun &uuml;bernimmt das Klassenmodul die Regie, indem Sie seine Prozedur <b>CreateDiagramDoc<\/b> aufrufen.<\/p>\n<p>Erst diese f&uuml;hrt auf Basis der erfolgten Einstellungen all jene <b>HTML5<\/b>-Aktionen durch und rendert das Diagramm im als Parameter &uuml;bergebenen Webbrowser-Objekt. Klappt dies, so gibt die Funktion <b>True<\/b> zur&uuml;ck. Andernfalls zeigt eine Meldung an, dass kein Diagramm erzeugt werden konnte, wobei allerdings keine Ausgabe des genauen Fehlergrunds erfolgt.<\/p>\n<h2>HTML5-Balkendiagrammklasse<\/h2>\n<p>Im Modul <b>clsHTMLDiagram<\/b> finden Sie nur etwas mehr als 200 Zeilen Code vor. Ein gro&szlig;er Teil geht dabei auf Variablendeklarationen und <b>Property<\/b>-Prozeduren drauf, die selbst keine Aktionen ausf&uuml;hren. Die zwei wirklich aktiven Prozeduren <b>CreateDiagramDoc<\/b> und deren Subroutine <b>PaintData<\/b> ben&ouml;tigen nur etwa 120 Zeilen. Gemessen am Ergebnis ist das extrem wenig, wenn man es mit anderen Grafikmethoden vergleicht. Wollten Sie dasselbe mit dem <b>Windows-API<\/b> erreichen, so k&ouml;nnten Sie bei <b>GDIPlus<\/b> locker mit etwa dem F&uuml;nffachen, bei <b>GDI32<\/b> mit dem Zehnfachen rechnen. Dennoch zeigen wir Ihnen hier nicht den kompletten Code des Moduls, sondern konzentrieren uns lieber auf das Wesentliche.<\/p>\n<p>Beginnen wir mit der Zuweisung des Recordsets an die Klasse &uuml;ber die <b>Property<\/b>-Methode <b>DataRecordset<\/b>. Die ist die einzige, die neben dem Speichern des Eigenschaftswerts in eine Membervariable (<b>m_RS<\/b>) noch eine zus&auml;tzliche Aufgabe erledigt, n&auml;mlich das Ermitteln des Wertebereichs der gelieferten Daten:<\/p>\n<pre><span style=\"color:blue;\">Property <span style=\"color:blue;\">Set<\/span> <\/span>DataRecordset(rs<span style=\"color:blue;\"> As <\/span>DAO.Recordset)\r\n     <span style=\"color:blue;\">Set<\/span> m_RS = rs\r\n     m_min = 9 ^ 10\r\n     m_max = -9 ^ 10\r\n     <span style=\"color:blue;\">With<\/span> m_RS\r\n         .MoveFirst\r\n         <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> .EOF\r\n             <span style=\"color:blue;\">If <\/span>rs(1).Value &gt; m_max<span style=\"color:blue;\"> Then<\/span> m_max = rs(1).Value\r\n             <span style=\"color:blue;\">If <\/span>rs(1).Value &lt; m_min<span style=\"color:blue;\"> Then<\/span> m_min = rs(1).Value\r\n             .Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Loop<\/span>\r\n         .MoveFirst\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">If <\/span>m_min = m_max<span style=\"color:blue;\"> Then<\/span> m_min = m_max - 10\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p><b>m_min<\/b> nimmt die untere Grenze des Wertebereichs auf, <b>m_max<\/b> die obere. Zu erw&auml;hnen w&auml;re an dieser Stelle, dass die Klasse zwingend ein ma&szlig;geschneidertes Recordset erwartet: Im ersten Feld vom Typ <b>Text<\/b> muss die Beschriftung des Datenpunkts stehen, im zweiten dessen Wert als Ganz- oder Gleitkommazahl. Gleitkommazahlen werden allerdings auf den Vorkommateil gerundet, was folglich die Anwendung auf Wertebereiche von mindestens <b>10<\/b> und aufw&auml;rts beschr&auml;nkt. Der Name der Felder im Recordset spielt indessen keine Rolle, da auf sie &uuml;ber deren Indizes <b>0<\/b> und <b>1<\/b> zugegriffen wird. Beispieldaten s&auml;hen damit etwa so aus:<\/p>\n<pre>Feld1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Feld2\r\nPolen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;12564,88\r\nFrankreich&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;32211,24\r\nBelgien&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8923,47<\/pre>\n<p>In einer Schleife werden alle Datens&auml;tze des Recordsets durchlaufen und der niedrigste Wert des zweiten Felds (<b>rs(1)<\/b>) in <b>m_min<\/b> gespeichert, der h&ouml;chste in <b>m_max<\/b>. Diese Werte werden sp&auml;ter ben&ouml;tigt, wenn es an die automatische Dimensionierung der Diagrammbalken und die Positionierung der Achsenbeschriftungen geht.<\/p>\n<p>Listing 3 zeigt nun die wesentlichen Bestandteile der Prozedur <b>CreateDiagramDoc<\/b> zum Erzeugen des Diagramms &#8211; oder genauer: der Anlage des <b>HTML5-Kontexts<\/b>. Erst der Aufruf der Subroutine <b>PaintData<\/b> zum Schluss f&uuml;hrt dann zum eigentlichen Zeichnen des Diagramms. Die meistens Vorg&auml;nge in der Routine entsprechen dem, was bereits zum <b>Listing 1<\/b> erl&auml;utert wurde.<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>CreateDiagramDoc(Browser<span style=\"color:blue;\"> As <\/span>WebBrowser)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>oDiv<span style=\"color:blue;\"> As <\/span>HTMLDivElement\r\n     <span style=\"color:blue;\">Dim <\/span>oElement<span style=\"color:blue;\"> As <\/span>IHTMLElement\r\n     \r\n     On Error Goto Fehler    \r\n     <span style=\"color:blue;\">Set<\/span> oDoc = Browser.Document\r\n     oDoc.body.setAttribute \"bgColor\", ColorToStr(vbWhite)\r\n     \r\n     <span style=\"color:blue;\">Set<\/span> oDiv = oDoc.createElement(\"div1\")\r\n     oDiv.Id = \"div1\"\r\n     oDoc.body.appendChild oDiv\r\n     \r\n     <span style=\"color:blue;\">Set<\/span> oElement = oDoc.createElement(\"canvas\")\r\n     oElement.Id = \"zeichnung\"\r\n     <span style=\"color:blue;\">Set<\/span> oCanvE = oElement\r\n     oCanvE.Width = m_Width: oCanvE.Height = m_Height\r\n     oDiv.appendChild oElement\r\n     \r\n     <span style=\"color:blue;\">Set<\/span> CTX = oDoc.getElementById(\"zeichnung\").getContext(\"2d\")\r\n     PaintData\r\n     CreateDiagramDoc = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">Exit Function<\/span>\r\nFehler:\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Diese Prozedur der Klasse erzeugt den HTML5-Kontext<\/span><\/b><\/p>\n<p>Die Klassenvariable <b>oDoc<\/b> nimmt das leere Dokument des Webbrowsers entgegen. &uuml;ber das Attribut <b>bgColor<\/b> wird nun die Hintergrundfarbe des <b>body<\/b>-Elements auf Wei&szlig; eingestellt. Folgend legt die Methode <b>createElement<\/b> einen <b>DIV<\/b>-Bereich im Dokument an, der als Container f&uuml;r die <b>HTML5<\/b>-Grafik dienen soll. Die wird &uuml;ber den <b>canvas<\/b> erzeugt, welcher mit der ID <b>zeichnung<\/b> versehen ist, dem <b>DIV<\/b>-Bereich, der nun in der Objektvariablen <b>oDiv<\/b> steht, hinzugef&uuml;gt (<b>appendChild<\/b>). Breite und H&ouml;he des <b>HTML5-Canvas<\/b> werden an dieser Stelle auch schon eingestellt (<b>Width<\/b>, <b>Height<\/b>). Zus&auml;tzlich erh&auml;lt die Klassenvariable <b>oCanvE<\/b> eine Kopie dieses <b>Canvas<\/b>-Objekts, weil ein Verweis auf ihn sp&auml;ter noch von anderen Prozeduren ben&ouml;tigt wird.<\/p>\n<p>Abschlie&szlig;end nimmt die Klassenvariable <b>CTX<\/b> noch den Grafikkontext von <b>HTML5<\/b> auf. Auch dieser wird sp&auml;ter noch weiterverwendet. Die Deklaration der drei Klassenvariablen sieht so aus:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>WithEvents oDoc<span style=\"color:blue;\"> As <\/span>HTMLDocument\r\n<span style=\"color:blue;\">Private <\/span>CTX<span style=\"color:blue;\"> As <\/span>ICanvasRenderingContext2D\r\n<span style=\"color:blue;\">Private <\/span>oCanvE<span style=\"color:blue;\"> As <\/span>IHTMLCanvasElement<\/pre>\n<p><b>oDoc<\/b> ist deshalb mit <b>WithEvents<\/b> deklariert, weil auf Ereignisse des Browser-Dokuments reagiert werden soll. So etwa auf das Klicken mit der rechten Maustaste, was &uuml;blicherweise das Kontextmen&uuml; des <b>Internet Explorers<\/b> hervorbringt. Die folgende kleine Ereignisprozedur aber unterbindet dies:<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>oDoc_oncontextmenu()<span style=\"color:blue;\"> As Boolean<\/span>\r\n     ''''\r\n<span style=\"color:blue;\">End Function<\/span> <\/pre>\n<p>Damit sind alle Vorarbeiten erledigt, der <b>HTML5<\/b>-Kontext angelegt, und es kann an das Zeichnen des Diagramms gehen. Das &uuml;bernimmt nun die Prozedur <b>PaintData<\/b>. <\/p>\n<p>Der Code dieser l&auml;nglichen Routine ist notwendigerweise stark mit Einr&uuml;ckungen versehen, weshalb seine Abbildung in einem Listing schwierig w&uuml;rde. Wir greifen die interessanteren Parts hier deshalb Schritt f&uuml;r Schritt auf. Eine komplette Erl&auml;uterung aller Zeilen w&uuml;rde sicher auch zu weit f&uuml;hren. Etwas externe Einarbeitung in <b>HTML5<\/b> ist zum Verst&auml;ndnis wohl n&ouml;tig. Sie finden im Netz zwar kaum Beispiele zu <b>HTML5<\/b> im Verein mit dem <b>MSHTML<\/b>-Objektmodell, daf&uuml;r aber ersch&ouml;pfend JavaScripte, deren &uuml;bersetzung nach VBA nicht allzu schwerf&auml;llt. Wir selbst haben das ja auch gemacht&#8230;<\/p>\n<p>Die Routine beginnt mit dem Errechnen einiger Werte, die f&uuml;r die Dimensionierung der Grafik ben&ouml;tigt werden:<\/p>\n<pre>n = m_RS.RecordCount\r\nxOffset = 40\r\nw1 = (m_Width - 20 - xOffset) \\ n\r\nx = xOffset\r\nyFactor = (m_Height - 50) \/ (m_max - m_min)\r\nyFactor = yFactor * 0.75<\/pre>\n<p>In <b>n<\/b> steht die Anzahl an Datens&auml;tzen des Recordset <b>m_RS<\/b>, welches zuvor ja &uuml;ber die Methode <b>DataRecordset<\/b> zugewiesen wurde. <b>xOffset<\/b> beschreibt den linken Rand des Diagramms in der Grafikfl&auml;che des <b>Canvas<\/b>. Aus der per <b>Property<\/b> gesetzten Breite in <b>m_Width<\/b> ergibt sich die Breite der Diagrammbalken (<b>w1<\/b>). Damit die Balken automatisch gem&auml;&szlig; der gelieferten Daten skaliert werden, steht in <b>yFactor<\/b> ein Skalierwert, der sich aus gew&uuml;nschter H&ouml;he (<b>m_Height<\/b>), der Range aus den Minimal- und Maximalwerten der Daten (<b>m_min<\/b>, <b>m_max<\/b>) und einem festen Offset von <b>50<\/b> errechnet. Damit der h&ouml;chste Balken nicht ganz bis zum oberen Rand des Diagramms reicht, wird der Faktor nochmals mit <b>0.75<\/b> verkleinert.<\/p>\n<p>Erst jetzt geht es an die Zeichenoperationen, die auf den <b>Canvas<\/b>-Kontext ausgef&uuml;hrt werden. Der steht in der Modulvariablen <b>CTX<\/b>, welche nun einem <b>With<\/b>-Block vorsteht:<\/p>\n<pre><span style=\"color:blue;\">With<\/span> CTX\r\n... (Methoden auf CTX)...\r\nEnd <span style=\"color:blue;\">With<\/span><\/pre>\n<p>Die folgenden Methoden mit einem voranstehenden Punkt-Operator beziehen sich also jeweils auf die Variable <b>CTX<\/b>. Das das Diagramm umrahmende Rechteck wird &uuml;ber zwei Methoden ausgegeben:<\/p>\n<pre>.FillStyle = ColorToStr(m_BackColor)\r\n.strokeRect xOffset, 20, m_Width - 20 - xOffset, m_Height - 50<\/pre>\n<p>Die F&uuml;llung des Rechtecks ergibt sich aus der gesetzten Hintergrundfarbe in <b>m_BackColor<\/b>. Das allerdings ist ein Long-Wert, w&auml;hrend <b>FillStyle<\/b> eine Farbe als Hexadezimal-String erwartet. Die Umwandlung &uuml;bernimmt die noch h&auml;ufiger ben&ouml;tigte Hilfsfunktion <b>ColorToStr<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>ColorToStr(AColor<span style=\"color:blue;\"> As Long<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>S<span style=\"color:blue;\"> As String<\/span>\r\n     S = Hex(AColor)\r\n     S = String(6 - <span style=\"color:blue;\">Len<\/span>(S), \"0\") & S\r\n     ColorToStr = \"#\" & <span style=\"color:blue;\">Mid<\/span>(S, 5) & <span style=\"color:blue;\">Mid<\/span>(S, 3, 2) & <span style=\"color:blue;\">Left<\/span>(S, 2)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Das Rechteck selbst malt die Methode <b>strokeRect<\/b> des <b>Canvas<\/b>-Kontexts. Sie erwartet Angaben zur Ursprungskoordinate links oben als erste zwei Parameter und dann noch Breite und H&ouml;he. Als Einheit verwendet <b>HTML5<\/b> hier immer <b>Pixel<\/b>. Die F&uuml;llung geschieht automatisch, indem die Farbe aus dem <b>FillStyle<\/b> entnommen wird und der Methode deshalb nicht extra als Parameter &uuml;bergeben werden muss.<\/p>\n<p>Im Folgenden geht es an die Anlage des Diagrammrasters, wobei wir hier nur horizontale Rasterlinien einsetzen. Ob diese &uuml;berhaupt gezeichnet werden sollen, entscheidet die Boolean-Variable <b>m_Raster<\/b>, die &uuml;ber die Eigenschaft <b>ShowRaster<\/b> der Klasse gesetzt wird:<\/p>\n<pre><span style=\"color:blue;\">If <\/span>m_Raster<span style=\"color:blue;\"> Then<\/span>\r\n    ...\r\nEnd if<\/pre>\n<p>Der dann gegebenenfalls ausgef&uuml;hrte Code:<\/p>\n<pre>.beginPath\r\n.FillStyle = \"#000000\"\r\ndifY = (m_Height - 50) \/ 10\r\n.strokeStyle = \"#A0A0A0\"\r\n.lineWidth = 1\r\ny = m_Height - 30\r\nFor i = 0 To 9\r\n     .moveTo xOffset, y\r\n     .lineTo m_Width - 20, y\r\n     .stroke\r\n     .fillText Round(m_max * i \/ 10, 0), 2, y + 4, xOffset\r\n     y = y - difY\r\n<span style=\"color:blue;\">Next<\/span> i<\/pre>\n<p>Mit <b>beginPath<\/b> sagen Sie dem Kontext, dass nun Zeichenanweisungen folgen, die nicht unmittelbar gerendert werden. Er merkt sich diese kumulativ und zeichnet erst dann, wenn die Methode <b>stroke<\/b> aufgerufen wird. Es sollen genau <b>10<\/b> Rasterlinien gezeichnet werden, was zum Offset <b>difY<\/b> f&uuml;hrt, der sich aus der H&ouml;he des Diagramms dividiert durch <b>10<\/b> ergibt. F&uuml;r die Linienst&auml;rke ist ein Pixel  &uuml;ber die Methode <b>lineWidth<\/b> angegeben, f&uuml;r die Linienfarbe &uuml;ber <b>strokeStyle<\/b> der Hexadezimalwert <b>#A0A0A0<\/b>, was einem hellgrau entspricht. <\/p>\n<p>Nun folgt eine Schleife, die zehnmal durchlaufen wird. Der Zeichenstift setzt &uuml;ber die Methode <b>moveTo<\/b> bei der Koordinate <b>xOffset<\/b> und <b>y<\/b> auf den <b>Canvas<\/b> auf. <b>y<\/b> war noch vor der Schleife auf den unteren Rand des Diagramms gesetzt worden. Bedenken Sie, dass sich aufsteigende y-Werte von oben nach unten im Canvas richten.<\/p>\n<p>Eine Line zeichnet die Methode <b>lineTo<\/b>, wobei der nur die Koordinate des Endpunkts als Parameter &uuml;bergeben werden muss. Der Startpunkt ist im Kontext gespeichert und wurde durch <b>moveTo<\/b> erzeugt. Nach dem <b>lineTo<\/b> verschiebt sich diese gespeicherte Koordinate &uuml;brigens automatisch zum Endpunkt der letzten Zeichenoperation.<\/p>\n<p>Ein Polygon k&ouml;nnten Sie folglich durch aneinandergereihte <b>lineTos<\/b> und die &uuml;bergabe der einzelnen Punkte erzeugen, ohne dazwischen <b>moveTo<\/b> bem&uuml;hen zu m&uuml;ssen.<\/p>\n<p>Die jeweilige Line wird durch Aufruf von <b>stroke<\/b> physisch gerendert. Neben der Rasterlinie gibt die Schleife auch gleich die Achsenbeschriftung aus, wof&uuml;r die Methode <b>fillText<\/b> zum Einsatz kommt. Der Achsenwert errechnet sich aus <b>m_max<\/b> und dem Schleifenz&auml;hler <b>i<\/b> geteilt durch <b>10<\/b> und gerundet auf Ganzzahlen. Die Zahl wird automatisch in einen String umgewandelt. Die zweiten und dritten Parameter der Methode geben wieder die Koordinate an, an der die linke obere Ecke des Textblocks ausgegeben werden soll.<\/p>\n<p>Als n&auml;chstes kommen die Datenbalken dran, die mit der Methode RECT gezeichnet werden. Wie die Rechtecke dann erscheinen werden, bestimmen wieder einige Einstellungen des Kontext-Styles:<\/p>\n<pre>.FillStyle = ColorToStr(m_DataColor)\r\n.strokeStyle = \"#000000\"\r\n.shadowColor = \"#C0C0C0\"\r\n.shadowOffsetX = 2\r\n.shadowOffsetY = 2\r\n.shadowBlur = 3\r\n.globalAlpha = 0.5<\/pre>\n<p>Der Hintergrund der Rechtecke kommt aus der Modulvariablen <b>m_DataColor<\/b>, die &uuml;ber das Property <b>DataColor<\/b> der Klasse gesetzt wurde. Die Linienfarbe (<b>strokeStyle<\/b>) ist schwarz (<b>#000000<\/b>). Nun k&ouml;nnen alle Zeichenoperationen von <b>HTML5<\/b> auch Schatten erzeugen!<\/p>\n<p>Das passiert, sobald Sie eine der <b>shadow<\/b>-Eigenschaften einstellen. <b>shadowColor<\/b> ist die Farbe des Schattens, <b>shadowOffsetX<\/b> dessen horizontale Verschiebung, <b>shadowOffsetY<\/b> die vertikale. <b>shadowBlur<\/b> bestimmt den Radius des Schattenwurfs.<\/p>\n<p>Schlie&szlig;lich k&ouml;nnen sowohl Linien, Hintergrund und Schatten auch transparent ausgegeben werden, indem Sie <b>globalAlpha<\/b> auf einen Wert kleiner <b>1<\/b> einstellen. Der hier verwendete Wert <b>0.5<\/b> zeichnet alles nur leicht transparent. Wir bereits fr&uuml;her erw&auml;hnt, verarbeitet <b>HTML5<\/b> diesen Wert offenbar exponentiell, weshalb <b>0.5<\/b> nicht gleich halbtransparent bedeutet, sondern deutlich weniger. In Abbildung 3 k&ouml;nnen Sie sehen, dass &uuml;ber diese Transparenz die Rasterlinien durch die Balken hindurch scheinen.<\/p>\n<p>Die folgende Schleife durchl&auml;uft alle Datens&auml;tze des Recordset <b>m_RS<\/b>:<\/p>\n<pre>x = xOffset\r\n<span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> m_RS.EOF\r\n     V = val(m_RS(1).Value)\r\n     .beginPath\r\n     <span style=\"color:blue;\">If <\/span>m_Random<span style=\"color:blue;\"> Then<\/span>\r\n         .FillStyle = ColorToStr(<span style=\"color:blue;\">RGB<\/span>(Rnd * 255, _\r\n             Rnd * 255, Rnd * 255))\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     .RECT x, m_Height - 30 - V * yFactor, w1, V * yFactor\r\n     .Fill\r\n     .stroke\r\n     x = x + w1\r\n     m_RS.Move<span style=\"color:blue;\">Next<\/span>\r\n<span style=\"color:blue;\">Loop<\/span><\/pre>\n<p>In die Variable <b>V<\/b> gelangt der Datenwert als <b>Double<\/b>-Typ, der zus&auml;tzlich mit der VBA-Funktion <b>Val<\/b> ausgewertet wird. Die Datenspalte k&ouml;nnte also auch vom Typ <b>Text<\/b> sein, was etwa beim Import aus einer <b>CSV<\/b>-Datei der Fall sein k&ouml;nnte.<\/p>\n<p>Die Hintergrundfarbe der Rechtecke wurde zwar bereits in <b>FillStyle<\/b> gesetzt, doch wenn Sie eine zuf&auml;llige Farbgebung &uuml;ber das <b>Property<\/b> <b>RandomColors<\/b> (<b>m_Random<\/b>) angewiesen haben, so greift die entsprechende Bedingung und die Eigenschaft wird fortw&auml;hrend neu gesetzt. Die neue Farbe errechnet sich &uuml;ber die <b>RGB<\/b>-Funktion und per <b>Rnd<\/b> gesetzten Zufallszahlen f&uuml;r die Farbkan&auml;le.<\/p>\n<p>Gezeichnet wird mit der Methode <b>RECT<\/b>. <b>strokeRect<\/b> h&auml;tte man auch verwenden k&ouml;nnen, doch dies h&auml;tte dann den im Kontext eingestellten <b>Style<\/b>, etwa den Schatten, nicht ber&uuml;cksichtigt. Erst die Kombination aus <b>RECT<\/b>, <b>fill<\/b> und <b>stoke<\/b> tut dies.<\/p>\n<p>Die H&ouml;he der Balken errechnet sich aus dem Datenwert in <b>V<\/b>, sowie aus dem zuvor ermittelten Skalierungsfaktor <b>yFactor<\/b>. Die Variable <b>x<\/b> bestimmt den horizontalen Versatz des Balkens und wird am Ende der Schleife fortlaufend erh&ouml;ht. In <b>w1<\/b> steht die anfangs ermittelte Breite der Balken.<\/p>\n<p>Das Raster, die Achsen und die Balken sind nun gezeichnet. Was fehlt sind der Diagrammtitel und die Datenbeschriftungen f&uuml;r die Balken. Ersteres ist schnell erledigt:<\/p>\n<pre>.globalAlpha = 1  ''''Transparenz ausgeschaltet\r\n.FillStyle = \"#000000\"\r\n.strokeStyle = \"#000000\"\r\n.Font = \"bold 18px Calibri\"\r\n.fillText m_Title, 20, 12, m_Width - 40\r\n.Fill<\/pre>\n<p>Die Schriftfarbe holt sich <b>fillText<\/b> wieder aus dem Kontext-Style und wird auf Schwarz eingestellt (<b>#000000<\/b>). Die Zeichenfarbe (<b>strokeStyle<\/b>) ebenfalls. Die Methode <b>fillText<\/b> hat n&auml;mlich eine Eigent&uuml;mlichkeit: Sie zeichnet zun&auml;chst nur die Umrandung des Texts. Die einzelnen Buchstaben sind aber nicht gef&uuml;llt. Das holt erst die Methode <b>fill<\/b> nach. Die Schriftart stellen Sie &uuml;ber die Eigenschaft <b>Font<\/b> ein.<\/p>\n<p>Diese erwartet einen String, der nicht nur den Namen der Schriftart enthalten kann, sondern zus&auml;tzlich auch gleich die Gr&ouml;&szlig;e und den Stil. Hier ist sie auf <b>18 Pixel<\/b> H&ouml;he gesetzt und &uuml;ber <b>bold<\/b> auf Fett. Die Syntax dieses Strings ist identisch zu der auch sonst in <b>CSS<\/b> verwendeten <b>Style<\/b>-Auszeichnung f&uuml;r Schriften. Im Netz finden Sie dazu ausf&uuml;hrliche Darstellungen.<\/p>\n<p>Zur Ausgabe der x-Achsen- und Datenbeschriftungen muss das Recordset abermals durchlaufen werden:<\/p>\n<pre>.Font = \"14px Calibri\"\r\n.beginPath\r\nx = xOffset: i = 0\r\n.shadowOffsetX = 0\r\n.shadowOffsetY = 0\r\n.shadowBlur = 0\r\n<span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> m_RS.EOF\r\n     .FillStyle = \"#000000\"   ''''X-Achse\r\n     .fillText m_RS(0).Value, x, m_Height - IIf((i <span style=\"color:blue;\">Mod<\/span> 2) _\r\n         = 0, 16, 4), w1 + 4\r\n     <span style=\"color:blue;\">If <\/span>m_ShowVals<span style=\"color:blue;\"> Then<\/span>\r\n         .FillStyle = \"#4080c0\"   ''''Datenwert\r\n         .fillText Round(m_RS(1).Value),  _\r\n             x + 2, m_Height - 33 - _\r\n             yFactor * m_RS(1).Value, w1 - 4\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     x = x + w1\r\n     i = i + 1\r\n     m_RS.Move<span style=\"color:blue;\">Next<\/span>\r\n<span style=\"color:blue;\">Loop<\/span><\/pre>\n<p>Erst wird die Schrift auf die Gr&ouml;&szlig;e <b>14 Pixel <\/b>eingestellt. Der zuvor gesetzte Schatten wird &uuml;ber die <b>shadow<\/b>-Methoden eliminiert. Die erste <b>fillText<\/b>-Anweisung zeichnet nun die x-Achsenbeschriftung. Steht <b>m_ShowVals<\/b> auf <b>True<\/b>, was &uuml;ber die Eigenschaft <b>ShowValues<\/b> der Klasse voreingestellt werden kann, so gibt eine weitere <b>fillText<\/b>-Anweisung den Datenwert aus.<\/p>\n<p>Die Schriftfarbe wird dabei abweichend auf <b>#4080C0<\/b> gesetzt, was einem mittleren Blau entspricht. Auf die Berechnung der jeweiligen Koordinaten f&uuml;r die Textausgabe gehen wir nicht weiter ein.<\/p>\n<p>Und das war es auch schon! Das fertige Diagramm ist gerendert und im Webbrowser-Steuerelement ausgegeben. Erzeugen Sie im Formular ein neues Diagramm mit anderer Einstellung der Optionsgruppe, so wird nur das Recordset aus einer neuen Umsatzabfrage ermittelt und die Farbgebung durch Setzen von <b>RandomColors<\/b> ver&auml;ndert.<\/p>\n<h2>Tortendiagramm<\/h2>\n<p>W&auml;hlen Sie die Ausgabe eines Tortendiagramms im Formular, so kommt ein komplett anderes Klassenmodul zum Einsatz, das Modul <b>clsDgmPieChart<\/b>. Das allerdings gleicht in seinen Eigenschaften v&ouml;llig dem des Balkendiagramms. Auch die anderen Methoden sind identisch. Im Code weicht lediglich die Prozedur <b>PaintData<\/b> ab, die eben ganz andere Zeichenoperationen ausl&ouml;st. Den Aufbau des Moduls m&uuml;ssen wir deshalb hier nicht nochmals wiederholt erl&auml;utern, sondern stellen eher die neuen <b>Canvas<\/b>-Methoden vor.<\/p>\n<p>Schauen wir uns dazu jenen Teil von <b>PaintData<\/b> an, der die Tortenst&uuml;cke zeichnet:<\/p>\n<pre><span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> m_RS.EOF\r\n     V = val(m_RS(1).Value)\r\n     .beginPath\r\n     <span style=\"color:blue;\">If <\/span>m_Random<span style=\"color:blue;\"> Then<\/span>\r\n         .FillStyle = ColorToStr(<span style=\"color:blue;\">RGB<\/span>(Rnd * 255, _\r\n             Rnd * 255, Rnd * 255))\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     a2 = a1 + (V \/ m_sum)\r\n     .arc x0, y0, r, 2 * PI * (a1 + 0.25), _\r\n         2 * PI * (a2 + 0.25), 0\r\n     .lineTo x0, y0\r\n     a1 = a2\r\n     .closePath\r\n     .Fill\r\n     m_RS.Move<span style=\"color:blue;\">Next<\/span>\r\n<span style=\"color:blue;\">Loop<\/span><\/pre>\n<p>Der Kern ist hier die Methode <b>arc<\/b>, welche einen Kreisabschnitt zeichnet. Die Berechnung ihrer Parameter ist nicht ganz einfach und erfordert einige trigonometrische Funktionen. Die Syntaxdefinition im Objektkatalog:<\/p>\n<pre><span style=\"color:blue;\">Sub <\/span>arc(x<span style=\"color:blue;\"> As Single<\/span>, y<span style=\"color:blue;\"> As Single<\/span>, radius<span style=\"color:blue;\"> As Single<\/span>, startAngle<span style=\"color:blue;\"> As Single<\/span>, endAngle<span style=\"color:blue;\"> As Single<\/span>, anticlockwise<span style=\"color:blue;\"> As Long<\/span>)<\/pre>\n<p><b>x<\/b> und <b>y<\/b> sind stellen die Koordinate des Kreismittelpunkts, die sich in unserem Beispiel in den Variablen <b>x0<\/b> und <b>y0<\/b> befindet. Der Radius des Kreises in <b>r<\/b> wurde zuvor aus der Ausdehnung des Diagramms errechnet:<\/p>\n<pre>r = (m_Height - 40) \/ 2.5 - 20<\/pre>\n<p>Komplizierter sieht es bei der Berechnung des Startwinkels (<b>a1<\/b>, <b>startAngle<\/b>) und Endwinkels (<b>a2<\/b>, <b>endAngle<\/b>) des Kreisabschnitts aus, welche beide im Bogenma&szlig; daherkommen m&uuml;ssen. Der Winkel des Abschnitts muss ja dem Datenwert in <b>V<\/b> entsprechen.<\/p>\n<p>F&uuml;r den letzten Kreisabschnitt muss <b>a2<\/b> den Wert <b>1<\/b> haben, was nach Multiplikation mit <b>2*PI<\/b> dem Winkel <b>360&deg;<\/b> entspricht. Also nimmt <b>a2<\/b> einen Teil der Datensumme ein, die wiederum in der Modulvariablen <b>m_sum<\/b> steht. Die wird bereits beim Zuweisen des Recordsets an die Klasse &uuml;ber die Methode <b>DataRecordset<\/b> errechnet, welche hier von der gleichnamigen Prozedur des Balkendiagramms abweicht:<\/p>\n<pre><span style=\"color:blue;\">Property <span style=\"color:blue;\">Set<\/span> <\/span>DataRecordset(rs<span style=\"color:blue;\"> As <\/span>DAO.Recordset)\r\n     <span style=\"color:blue;\">Set<\/span> m_RS = rs\r\n     m_sum = 0\r\n     <span style=\"color:blue;\">With<\/span> m_RS\r\n         .MoveFirst\r\n         <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> .EOF\r\n             m_sum = m_sum + rs(1).Value\r\n             .Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Loop<\/span>\r\n         .MoveFirst\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Property<\/span><\/pre>\n<p>Die Routine addiert einfach alle Datenwerte des Recordsets! Der Wert eines einzelnen Datensatzes ist damit genau der Bruchteil der Summe, der dem Winkel des Kreisbogens entspricht. Auf <b>arc<\/b> folgt noch eine <b>lineTo<\/b>-Anweisung, die jeweils eine Linie zum Kreismittelpunkt zeichnet und damit das Tortenst&uuml;cke verdeutlicht. Auch hier kann auf das Setzen des Startpunktes per <b>moveTo<\/b> verzichtet werden, weil die Koordinate des Kreisbogenendes immer als aktuelle Koordinate im Canvas gespeichert wird. Die Fl&auml;che des Tortenst&uuml;cks wird schlie&szlig;lich &uuml;ber die <b>fill<\/b>-Anweisung mit Farbe gef&uuml;llt.<\/p>\n<p>Was beim Balkendiagramm die x-Achsenbeschriftung war, ist beim Tortendiagramm, das Sie fertiggestellt in Bild 8 begutachten k&ouml;nnen, die Kreisbeschriftung. F&uuml;r die Ausgabe der Textpositionen m&uuml;ssen hier deshalb wieder trigonometrische Funktion verwendet werden, deren Berechnung wir nicht vertiefen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_01\/frmDgmPie2.png\" alt=\"So sieht das dynamisch erzeugte HTML5-Tortendiagramm im Formular frmHTML5Diagram aus\" width=\"650\" height=\"458,1014\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: So sieht das dynamisch erzeugte HTML5-Tortendiagramm im Formular frmHTML5Diagram aus<\/span><\/b><\/p>\n<p>Interessant ist jedoch eine Kleinigkeit, die wir bisher f&uuml;r die Methode <b>fillText<\/b> unterschlagen haben. Die erwartet n&auml;mlich als letzten Parameter die maximale Breite in Pixel, die der gerenderte Text einnehmen darf. Ist er l&auml;nger, als diese Angabe, so bricht die Methode den Text um!<\/p>\n<p>Eigentlich ist der Parameter nach der <b>HTML5<\/b>-Definition optional, doch Microsoft hat ihn im <b>MSHTML<\/b>-Objektmodell als obligatorisch eingebaut &#8211; Sie k&ouml;nnen ihn also nicht weglassen.<\/p>\n<p>Woher aber wissen Sie, wie breit ein Text in der eingestellten Schriftart werden wird, um diesen Parameter zu best&uuml;cken<\/p>\n<p>Da hilft uns direkt eine Methode des <b>Canvas<\/b> weiter, der diese Berechnung erm&ouml;glicht. Der Funktion <b>measureText<\/b> &uuml;bergeben Sie den gew&uuml;nschten String und erhalten einen <b>MSHTML<\/b>-Typ <b>ICanvasTextMetrics<\/b>. Beispiel:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>Metr<span style=\"color:blue;\"> As <\/span>ICanvasTextMetrics\r\n<span style=\"color:blue;\">Set<\/span> Metr = CTX. measureText(\"Access im Unternehmen\")\r\n<span style=\"color:blue;\">Debug.Print<\/span> Metr.Width<\/pre>\n<p>oder abgek&uuml;rzt:<\/p>\n<pre>W = CTX. measureText(\"Access im Unternehmen\").Width<\/pre>\n<p>Diesen Wert k&ouml;nnen Sie nun als letzten Parameter f&uuml;r <b>fillText<\/b> angeben. Nat&uuml;rlich k&ouml;nnen Sie zuvor auch Plausibilit&auml;ts-Checks einbauen, um etwa zu ermitteln, ob der Text dabei &uuml;ber den Rand des Canvas hinausragte. K&uuml;rzen Sie dann die Breite entsprechend und lassen Sie den Text damit umbrechen.<\/p>\n<h2>Drucken und Speichern der HTML5-Grafik<\/h2>\n<p>Nun liegt das Diagramm oder eine sonst wie erzeugte Grafik im Webbrowser-Steuerelement vor und Sie m&ouml;chten diese ausdrucken oder als Datei exportieren. Das eine ist leichter, als das andere.<\/p>\n<p>Drucken k&ouml;nnen Sie &uuml;ber eine eingebaute Methode des Internet Explorers. F&uuml;r das Diagramm sieht die dem Button <b>cmdPrint<\/b> im Formular hinterlegte Ereignisprozedur im Prinzip so aus:<\/p>\n<pre>If <span style=\"color:blue;\">Not<\/span> Me!ctlWeb.Object.Document  Is Nothing then\r\n     Me!ctlWeb.Object.Document ExecCommand \"print\", <span style=\"color:blue;\">True<\/span>\r\nEnd if<\/pre>\n<p>Falls ein g&uuml;ltiges Dokument vorliegt, so f&uuml;hrt seine Methode <b>ExecCommand<\/b> eine Anweisung aus, die Sie als String angeben. Die Methode gleicht im Prinzip der <b>RunCommand<\/b>-Methode von Access und f&uuml;hrt etwa Men&uuml;befehle aus. Mit <b>print<\/b> drucken Sie das Dokument so, als riefen Sie den Drucken-Befehl im Men&uuml; des Internet Explorers auf. Der zweite Parameter mit <b>True<\/b> bedeutet, dass hierbei der Druckerauswahldialog erscheinen soll. Mehr Einfluss auf den Druckvorgang haben Sie ansonsten nicht.<\/p>\n<p>Das Abspeichern bereitet etwas mehr Kopfzerbrechen. Zwar k&ouml;nnten Sie hierf&uuml;r ebenfalls <b>ExecCommand<\/b> benutzen und als Anweisung ein <b>SaveAs<\/b> absetzen, was den <b>Speichern als<\/b>-Dialog zutage f&ouml;rdert.<\/p>\n<p>M&ouml;chten Sie jedoch ohne Dialog direkt ins Dateisystem abspeichern, so m&uuml;ssen Sie irgendwie an das <b>HTML5<\/b>-Bildobjekt des Webbrowsers kommen. Derlei gibt es indessen in seinem Objektmodell nicht.<\/p>\n<p>Stattdessen kennt das <b>HTMLCanvas<\/b>-Element (nicht dessen <b>Kontext<\/b>-Objekt <b>CTX<\/b>!) eine Funktion <b>toDataURL<\/b>, die seinen Bildinhalt als Datenstrom zur&uuml;ckliefert. Die Diagrammklassen speichern etwa einen Verweis auf das <b>Canvas<\/b>-Element beim Aufruf der Methode <b>CreateDiagramDoc<\/b> in der Variablen <b>oCanvE<\/b> vom Typ <b>IHTMLCanvasElement<\/b>. Die Daten erhalten Sie somit dergestalt:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>sData<span style=\"color:blue;\"> As String<\/span>\r\nsData = oCanvE.toDataURL(&lt;Typ&gt;,&lt;Qualit&auml;t&gt;)<\/pre>\n<p>Sie sehen, Sie erhalten mit der Funktion einen String und nicht etwa ein Byte-Array, das direkt in eine Datei geschrieben werden k&ouml;nnte. Als Parameter &uuml;bergeben Sie den gew&uuml;nschten Dateityp. Das muss ein <b>MIME<\/b>-K&uuml;rzel sein, wie etwa <b>image\/jpeg<\/b> oder <b>image\/png<\/b>.<\/p>\n<p>Falls Sie ein <b>JPG<\/b> w&uuml;nschen, so k&ouml;nnen Sie dessen Qualit&auml;t mit dem zweiten Parameter <b>Qualit&auml;t<\/b> im Bereich von 0 bis <b>1<\/b> einstellen, wobei <b>1<\/b> die h&ouml;chste Stufe darstellt. Aber auch bei <b>PNG<\/b> ist der Parameter nicht optional, obwohl er ignoriert wird. Setzen Sie dann einfach <b>0<\/b> ein.<\/p>\n<p>Was nun im Daten-String steht sind die <b>base64<\/b>-kodierten Dateidaten. Sie kennen das eventuell aus E-Mails, in denen Anh&auml;nge ebenfalls in diesem Format &uuml;bertragen werden. Der String sieht etwa so aus:<\/p>\n<pre>data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAA6IAAAIECAYAAAADypPAAAAAA ...<\/pre>\n<p>Damit stehen wir vor der Aufgabe, diesen String zu dekodieren. Gl&uuml;cklicherweise muss man dazu keine komplexen Prozeduren bem&uuml;hen, weil Microsoft in der <b>MSXML<\/b>-Bibliothek einen solchen Decoder eingebaut hat. Setzen Sie also einen Verweis auf die <b>MSXML2<\/b>-Bibliothek und verwenden Sie nun ein neu instanziiertes <b>XML-Dokument<\/b>. Die Routine <b>DecodeBase64<\/b> im Modul <b>mdlCanvas<\/b> der Beispieldatenbank verdeutlicht den Vorgang:<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>DecodeBase64(BaseStr<span style=\"color:blue;\"> As String<\/span>) _\r\n        <span style=\"color:blue;\"> As Byte<\/span>()\r\n     <span style=\"color:blue;\">Dim <\/span>oXML<span style=\"color:blue;\"> As <\/span><span style=\"color:blue;\">New<\/span> MSXML2.DOMDocument\r\n     <span style=\"color:blue;\">Dim <\/span>oElem<span style=\"color:blue;\"> As <\/span>MSXML2.IXMLDOMElement\r\n     <span style=\"color:blue;\">Set<\/span> oXML = <span style=\"color:blue;\">New<\/span> MSXML2.DOMDocument\r\n     <span style=\"color:blue;\">Set<\/span> oElem = oXML.createElement(\"tmp\")\r\n     <span style=\"color:blue;\">With<\/span> oElem\r\n         .DataType = \"bin.base64\"\r\n         .Text = BaseStr\r\n         DecodeBase64 = .NodeTypedValue\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die Objektvariable <b>oXML<\/b> erh&auml;lt ein neues XML-Dokument. In diesem erzeugt die Funktion <b>createElement<\/b> ein unspezifisches <b>XML-Element<\/b> namens <b>tmp<\/b>, das in der Variablen <b>oElem<\/b> zwischengespeichert wird. F&uuml;r dieses Element geben Sie im Attribut <b>DataType<\/b> den Wert <b>bin.base64<\/b> an und speisen es &uuml;ber die Eigenschaft <b>Text<\/b> mit dem der Funktion &uuml;bergebenen Datenstrom in <b>baseStr<\/b>.<\/p>\n<p>Und schon k&ouml;nnen Sie &uuml;ber die Eigenschaft <b>NodeTypedValue<\/b> die dekodierten Daten abrufen. Die Dekodierung geschieht dabei intern automatisch. Die R&uuml;ckgabe erfolgt in einem Byte-Array.<\/p>\n<p>Der Hilfsfunktion m&uuml;ssen Sie allerdings die reinen Daten &uuml;bergeben, die vom <b>Header<\/b> befreit sind. Der steht am Anfang und hat im Beispiel den Text <b>data:image\/png;base64<\/b>, gefolgt von einem Komma. Und nach diesem beginnen die eigentlichen Daten. Was nun im Byte-Array steht, ist tats&auml;chlich die Datei im RAM, die Sie etwa &uuml;ber ein <b>Open-Put<\/b>-Konstrukt als Datei abspeichern. Der vollst&auml;ndige Code der Prozedur <b>SaveCanvasPic<\/b> steht in Listing 4. Sie &uuml;bergeben ihr einen Verweis auf das <b>HTMLCanvas<\/b>-Element und optional den gew&uuml;nschten Dateityp in Form des <b>MIME<\/b>-K&uuml;rzels. Voreingestellt ist hier das verlustlose <b>PNG<\/b>. Geben Sie f&uuml;r den Parameter <b>Preview<\/b> ein <b>True<\/b> an, so startet die Funktion anschlie&szlig;end automatisch die Windows-Bildanzeige auf die erzeugte Datei oder einen anderen Bildeditor, der mit der Dateiendung bei Ihnen assoziiert ist.<\/p>\n<pre><span style=\"color:blue;\">Function <\/span>SaveCanvasPic(oCanv<span style=\"color:blue;\"> As <\/span>IHTMLCanvasElement, _\r\n         <span style=\"color:blue;\">Optional<\/span> sType = \"image\/png\", _\r\n         <span style=\"color:blue;\">Optional<\/span> Preview<span style=\"color:blue;\"> As Boolean<\/span> = <span style=\"color:blue;\">True<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>sData<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bin()<span style=\"color:blue;\"> As Byte<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>quality<span style=\"color:blue;\"> As Single<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>sExt<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>sPath<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>oFD<span style=\"color:blue;\"> As <\/span>Office.FileDialog                \r\n     <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n     sExt = <span style=\"color:blue;\">Replace<\/span>(sType, \"image\/\", \"\")\r\n     <span style=\"color:blue;\">If <\/span>sExt = \"jpeg\"<span style=\"color:blue;\"> Then<\/span> quality = 0.8 Else quality = 1\r\n     sData = oCanv.toDataURL(sType, quality)\r\n     sData = <span style=\"color:blue;\">Mid<\/span>(sData, <span style=\"color:blue;\">InStr<\/span>(1, sData, \",\") + 1)\r\n     bin = DecodeBase64(sData)    \r\n     <span style=\"color:blue;\">Set<\/span> oFD = Application.FileDialog(msoFileDialogSaveAs)\r\n     <span style=\"color:blue;\">With<\/span> oFD\r\n         .AllowMultiSelect = <span style=\"color:blue;\">False<\/span>\r\n         .InitialView = msoFileDialogViewDetails\r\n''''        .Filters.Add \"Bilddatei\", \"*.\" & sExt\r\n''''        .FilterIndex = 1\r\n         .InitialFileName = CurrentProject.Path & \"\\HTML5.\" & sExt\r\n         .Title = \"Save Image...\"\r\n         <span style=\"color:blue;\">If <\/span>.Show<span style=\"color:blue;\"> Then<\/span> sPath = .SelectedItems(1)\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(sPath) = 0<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Function<\/span>    \r\n     Open sPath For Binary<span style=\"color:blue;\"> As <\/span>#1\r\n     Put #1, , bin\r\n     Close #1\r\n     SaveCanvasPic = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">If <\/span>Preview<span style=\"color:blue;\"> Then<\/span>\r\n         ShellExecute 0&, \"open\", sPath, vbNullString, vbNullString, 1&\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Exit Function<\/span>    \r\nFehler:\r\n     <span style=\"color:blue;\">MsgBox<\/span> Err.Description, vbExclamation, \"SaveCanvasPic\"\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Die Routine speichert eine HTML5-Grafik als Datei<\/span><\/b><\/p>\n<p>Die Dateiendung in <b>sExt<\/b> wird direkt aus dem <b>MIME<\/b>-String erhalten. Handelt es sich hierbei um <b>JPEG<\/b>, so wird der Qualit&auml;tsfaktor fest auf <b>0.8<\/b> eingestellt, was in g&auml;ngigen Bildeditoren 80% entspricht. Sie k&ouml;nnen diesen Wert nat&uuml;rlich selbst bestimmen. Anschlie&szlig;end holt die Methode <b>toDataURL<\/b> auf den &uuml;bergebenen <b>Canvas<\/b> die <b>HTML5<\/b>-Bilddaten in die Variable <b>sData<\/b>. Von diesem String wird der Anfang bis zum Komma &uuml;ber die <b>Mid<\/b>-Funktion entfernt. Das Byte-Array <b>bin<\/b> nimmt dann die mithilfe der Funktion <b>DecodeBase64<\/b> dechiffrierten Bin&auml;rdaten auf. <\/p>\n<p>Im n&auml;chsten Block &ouml;ffnet sich der <b>Office-FileDialog<\/b> im Modus <b>SaveAs<\/b> zur Auswahl eines Dateinamens, unter dem die Datei abgespeichert werden soll. Wurde der Dialog nicht abgebrochen, so ist in <b>sPath<\/b> nun die gew&auml;hlte Pfadangabe gespeichert. Die Datei wird per <b>Open<\/b>-Anweisung im Bin&auml;rmodus ge&ouml;ffnet und der Inhalt des Byte-Arrays <b>bin<\/b> in sie geschrieben.<\/p>\n<p>Wurde im Parameter <b>Preview<\/b> die Bildanzeige gew&uuml;nscht, so &ouml;ffnet die API-Funktion <b>ShellExecute<\/b> abschlie&szlig;end die soeben erstellte Datei mit dem assoziierten Programm, was h&auml;ufig die Windows-Bildanzeige sein d&uuml;rfte.<\/p>\n<p>Statt der Speicherung in eine Datei k&ouml;nnen Sie die Daten nat&uuml;rlich auch direkt in ein <b>OLE-Feld<\/b> einer Tabelle schreiben. Sie haben dabei die Auswahl, ob Sie daf&uuml;r das dekodierte Byte-Array nehmen, oder gleich den <b>base64<\/b>-Daten-String, f&uuml;r den sich dann eher ein Memo-Feld eignen w&uuml;rde. Allerdings ist der <b>base64<\/b>-String deutlich gr&ouml;&szlig;er, als die dekodierte <b>JPG<\/b>&#8211; oder <b>PNG<\/b>-Datei. Abhilfe kann hier aber eine Kompression des Strings schaffen, wie Sie sie im Beitrag <b>Datenkompression leicht gemacht<\/b> dieser Ausgabe finden. Das Modul <b>mdlCanvas<\/b> der Beispieldatenbank enth&auml;lt daf&uuml;r auch gleich die Routinen <b>CompressRTL<\/b> und <b>DecompressRTL<\/b>.<\/p>\n<h2>Zusammengefasst<\/h2>\n<p>[50618]<\/p>\n<p>Aus einem Webbrowser-Steuerelement k&ouml;nnen Sie in Ihren Formularen mithilfe von HTML5 und der MSHTML-Bibliothek ein Grafiksteuerelement machen. Mit wenig Aufwand lassen sich programmgesteuert Bilder erzeugen, abspeichern und ausdrucken. Obwohl der Umfang der Anweisungen von HTML5 recht &uuml;berschaubar ist, vermisst man nur wenig. Neben Zeichenoperationen f&uuml;r Linien, Rechtecke, Kreise, Bezierkurven und F&uuml;llungen haben Sie Features in der Hand, wie Schattenwurf oder Transparenz, die auf andere Weise &#8211; etwa per API &#8211; nur schwer nachzubilden w&auml;ren. <\/p>\n<p>[50634]<\/p>\n<p>Hier haben wir zur Demonstration nur einige Diagramme per HTML5 erstellt, damit die grundlegenden Vorg&auml;nge verdeutlicht werden. Das Thema konnte damit nur angerissen werden. Selbstverst&auml;ndlich lassen sich auch beliebige andere Grafiken erzeugen. <\/p>\n<p>[51182]<\/p>\n<p>Dass sich HTML5 im Browser auch interaktiv verwenden l&auml;sst, m&ouml;chten wir Ihnen aber nicht vorenthalten. In einem Folgebeitrag zu HTML5 mit dem Titel <b>Mit HTML5 zeichnen und malen <\/b>(<b>www.access-im-unternehmen.de\/1117<\/b>) zeigen wir, wie sich auch eine einfache Malanwendung realisieren l&auml;sst. Interessant ist das vor allem f&uuml;r Nutzer von Zeichentabletts, von Touchscreens oder mobilen Windows-Ger&auml;ten. Dabei kommen Mausereignisse des Webbrowser-Dokuments ins Spiel, die ausgewertet und in Linien umgesetzt werden.<\/p>\n<p>[51429]<\/p>\n<p>All dies kann auch mit einem anderen Modell erreicht werden, dem eingangs erw&auml;hnten SVG. Auch dieses werden wir in einer kommenden Ausgabe vorstellen.<\/p>\n<p>[51935]<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>HTMLImage.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/D0BD5704-F935-4C49-92DA-0E3D4E259FCA\/aiu_1116.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wenn es um das Erzeugen von Grafiken geht, l&auml;sst Access Sie weitgehend im Regen stehen. Maximal die Anzeige von Bilddateien &uuml;ber das Bild- oder Anlagesteuerelement ist in Formularen und Berichten vorgesehen. F&uuml;r alles dar&uuml;ber hinaus kommen Sie um den Einsatz aufw&auml;ndiger Windows-API-Funktionen nicht herum. Wir zeigen hier aber, dass Sie auch mithilfe des Webbrowser-Steuerelements und HTML5 auf einfache Weise zu verbl&uuml;ffenden Ergebnissen kommen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66012018,662018,44000027],"tags":[],"class_list":["post-55001116","post","type-post","status-publish","format-standard","hentry","category-66012018","category-662018","category-Loesungen"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>HTML5 als Grafikkomponente - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HTML5 als Grafikkomponente\" \/>\n<meta property=\"og:description\" content=\"Wenn es um das Erzeugen von Grafiken geht, l&auml;sst Access Sie weitgehend im Regen stehen. Maximal die Anzeige von Bilddateien &uuml;ber das Bild- oder Anlagesteuerelement ist in Formularen und Berichten vorgesehen. F&uuml;r alles dar&uuml;ber hinaus kommen Sie um den Einsatz aufw&auml;ndiger Windows-API-Funktionen nicht herum. Wir zeigen hier aber, dass Sie auch mithilfe des Webbrowser-Steuerelements und HTML5 auf einfache Weise zu verbl&uuml;ffenden Ergebnissen kommen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-13T21:18:05+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"45\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"HTML5 als Grafikkomponente\",\"datePublished\":\"2020-05-13T21:18:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/\"},\"wordCount\":7797,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/6ed28b711c004f1fbb4600f8fa986ad3\",\"articleSection\":[\"1\\\/2018\",\"2018\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/\",\"name\":\"HTML5 als Grafikkomponente - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/6ed28b711c004f1fbb4600f8fa986ad3\",\"datePublished\":\"2020-05-13T21:18:05+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/6ed28b711c004f1fbb4600f8fa986ad3\",\"contentUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/6ed28b711c004f1fbb4600f8fa986ad3\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/HTML5_als_Grafikkomponente\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"HTML5 als Grafikkomponente\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"HTML5 als Grafikkomponente - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/","og_locale":"de_DE","og_type":"article","og_title":"HTML5 als Grafikkomponente","og_description":"Wenn es um das Erzeugen von Grafiken geht, l&auml;sst Access Sie weitgehend im Regen stehen. Maximal die Anzeige von Bilddateien &uuml;ber das Bild- oder Anlagesteuerelement ist in Formularen und Berichten vorgesehen. F&uuml;r alles dar&uuml;ber hinaus kommen Sie um den Einsatz aufw&auml;ndiger Windows-API-Funktionen nicht herum. Wir zeigen hier aber, dass Sie auch mithilfe des Webbrowser-Steuerelements und HTML5 auf einfache Weise zu verbl&uuml;ffenden Ergebnissen kommen.","og_url":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-13T21:18:05+00:00","og_image":[{"url":"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"45\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"HTML5 als Grafikkomponente","datePublished":"2020-05-13T21:18:05+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/"},"wordCount":7797,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#primaryimage"},"thumbnailUrl":"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3","articleSection":["1\/2018","2018","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/","url":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/","name":"HTML5 als Grafikkomponente - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#primaryimage"},"thumbnailUrl":"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3","datePublished":"2020-05-13T21:18:05+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#primaryimage","url":"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3","contentUrl":"http:\/\/vg06.met.vgwort.de\/na\/6ed28b711c004f1fbb4600f8fa986ad3"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/HTML5_als_Grafikkomponente\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"HTML5 als Grafikkomponente"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001116","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001116"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001116\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}