Webpalette
Dynamische Webanwendungen entwickeln viele Webmaster seit Jahren mit Perl und vielleicht etwas weniger langer Zeit dem aufs Web konzentrierten PHP. Die Sprache kann mehr als nur die Verbindung zu Datenbanken herstellen - unter anderem Grafiken dynamisch erzeugen.
In den letzten Jahren hat die Scriptsprache PHP große Verbreitung für dynamische Webanwendungen erlangt. Das Spektrum der Anwendungen reicht von der typischen MySQL-Abfrage mit HTML-Ausgabe bis hin zu Content-Management-Systemen mit Multiuser-Fähigkeit, Staging-Mechanismen und XML-Parsing. Neben dieser eher textlastigen Methode der Datenvisualisierung kann man jedoch mit PHP mehr erreichen: unter anderem Grafikanwendungen.
PHP hat hier von bestehenden APIs für andere Sprachen profitiert und stellt momentan Support für zwei bekannte Grafikbibliotheken zur Verfügung: GD und ImLib. Beide basieren auf den jeweiligen C-APIs, die (mehr oder minder vollständig) für PHP portiert sind. Der Funktionsumfang von GD und ImLib ist in PHP recht ähnlich, GD jedoch momentan stärker verbreitet und der De-facto-Standard für Grafikoperationen in dieser Sprache, sodass sie für das erste Beispiel herhalten muss.
Bunter Parser: PHP und GD verheiraten
Um GD verwenden zu können, muss die Bibliothek in der jeweils aktuellen Version installiert sein, was im Folgenden als gegeben vorausgesetzt sei. GD 2.0.1 ist trotz des Betastatus erstaunlich stabil, die Bibliothek haben Hoster häufig vorinstalliert. Sollte das nicht der Fall sein und die Möglichkeit bestehen, PHP nach eigenen Vorstellungen zu kompilieren, lässt sich GD mit -with-gd in PHP einkompilieren. Je nach gewünschtem Funktionsumfang empfehlen sich noch -with-jpeg-dir (JPEG-Bibliothek, sofern installiert) und -with-png-dir (PNG-Unterstützung, benötigt PNGLib und ZLib) sowie -with-freetype-dir für die FreeType-TTF-Unterstützung. Ausführliche Anleitungen zum Kompilieren von PHP mit GD sowie sämtliche benötigten Bibliotheken finden sich bei Jörg Baach und beim PHP-Projekt (siehe den Kasten ‘Online-Ressourcen’).
Version 2.0.1 der GD-Bibliothek hält www.rpmfind.net in verschiedenen Ausprägungen vor. Um GD in PHP einzukompilieren, kann es erforderlich sein, eine neue libphp4 für den Apache zu erstellen. Vor dem make; make install erfordert die Konfiguration ein paar spezielle Optionen, damit die True-Type-Fonts berücksichtigt werden.
configure -with-gdbm \
-with-mysql \
-with-xml \
-with-gd=yes \
-enable-gd-native-ttf \
-with-ttf \
-with-freetype-dir=/usr \
-with-apxs=/usr/sbin/apxs
Die letzte Option stellt sicher, dass der Compiler PHP als Dynamic Shared Object (DSO) erstellt. Die vier davorstehenden betreffen GD.
Thumbnail-Galerie mit GD
Für erste Gehversuche mit GD sind einige TTF-Dateien sowie ein paar Bilder im PNG- oder JPG-Format erforderlich. True-Type-Fonts für den privaten Gebrauch hat unter anderem Microsoft zur Verfügung gestellt; einige sind im JPGraph-Paket enthalten (siehe beide im Kasten ‘Online-Ressourcen’). Die ersten Listings beziehen sich auf GD, danach zeigt ein kurzer Blick auf das fortgeschrittene JPGraph, was in diesem Diagrammpaket steckt.
Als typische Anwendung für einfache Bildverarbeitung in PHP dient eine Thumbnail-Galerie, die - basierend auf einem übergebenen Dateinamen - eine Miniaturausführung der Grafik erstellt, sie speichert und anzeigt.
Bilddateien behandelt PHP als Filehandles - Pointer auf die tatsächlichen Dateien - und liest sie zunächst ein.
<? $quellbild = ImageCreateFromJPEG("image.jpg"); ?>
$quellbild ist das Handle für alle folgenden Operationen. Analog kann ImageCreateFromPNG() ein PNG einlesen und ImageCreateTrueColor ($breite, $hoehe) ein leeres Bild-Handle mit den Maßen $breite, $hoehe erstellen.
Ist ein Filehandle vorhanden, schreibt der Programmierer einige Informationen zur späteren Verwendung in Variablen. Aus den so erhaltenen Werten für Höhe und Breite des Ursprungsbildes sowie der neuen Breite $breite_neu kann er leicht die Höhe des skalierten Bildes errechnen.
<?php
$breite_neu = 200;
$breite = ImageSX($quellbild);
$hoehe = ImageSY($quellbild);
$skalierungsfaktor = $breite/$breite_neu;
$hoehe_neu = $hoehe/$skalierungsfaktor;
?>;
Da ein zweites Bild (der Thumbnail) erstellt, gespeichert und angezeigt werden soll, wird ein zweites Filehandle angelegt, das als Höhe und Breite die soeben errechneten Werte erhält.
$zielbild = ImageCreateTrueColor($breite, $hoehe);
GD differenziert zwischen True-Color- sowie Indexed-Color-Bildern und stellt für beide Funktionen zur Verfügung. In der Regel dürfte ein Entwickler bei GD 2.0.1 True-Color-Funktionen verwenden, weil die Bildqualität Indexed-Color überlegen ist. Speziell die Skalierung von Bildern lässt sich nur mit TrueColor in befriedigender Qualität erreichen. GD 2.0.1 führt zusätzlich zu ImageCopyResized() eine Funktion ein, die außer der Skalierung eine Interpolation vornimmt: ImageCopyResampled().
ImageCopyResampled($zielbild, $quellbild,
0, 0, 0, 0, $breite_neu, $hoehe_neu, $breite, $hoehe);
kopiert den Inhalt des alten Bildes in das neue und skaliert nach den letzten vier Faktoren. $breite_neu und $hoehe_neu geben die X- und Y-Werte der zu generierenden Miniaturgrafik an.
Nach dem Skalieren des Zielbilds auf die gewünschte Auflösung lässt sich eine kurze Zeile Text hinzufügen, um ähnlich einem Wasserzeichen unbefugte Verwendung zu vermeiden. Als Erstes definiert der Entwickler die gewünschte Farbe - GD behandelt Farben ähnlich wie Bilder, mit einer Art von Filehandles. Die Funktion ImageColorAllocate() weist einem Handle eine Farbe zu, etwa $weiss = ImageColorAllocate($zielbild, 255, 255, 255).
Für das Einfügen von Text in Bildern kennt GD zwei Funktionen, ImageString() und ImageTTFText(). Während erstere von Zusatzbibliotheken unabhängig ist, da sie auf eingebaute Schrifttypen zurückgreift, ist ImageTTFText() ausschließlich für den Einsatz mit FreeType gedacht und deshalb auf TTF-Schriften angewiesen.
ImageTTFText($im2, 7, 0, 5,
$hoehe_neu - 10, $weiss, $font, $i);
schreibt den Wert des in $i übergebenen Dateinamens in Schriftgröße 7 mit einem Abstand von 5 Pixeln zum linken und 10 Pixeln zum unteren Rand auf das Bild. Dabei wird die Schriftdatei der Variablen $font in der vorher definierten Farbe $weiss verwendet. Die Pixelangaben sind relativ zur ‘Baseline’ der Schrift: das heißt, die Schleifen eines ‘g’ können unterhalb der Y-Angabe liegen.
Wenn alle gewünschten Operationen ausgeführt sind, kann PHP den Thumbnail über den Browser ausgeben oder speichern. Im Falle einer Thumbnail-Sammlung ist es angebracht, die Bildchen nur einmal zu erstellen und mit einer Überprüfung auf Existenz der Datei die gespeicherten Bilder auszugeben, anstatt sie jedes Mal zu generieren. Die Erstellung von Thumbnails mittels ImageCopyResampled() ist eine CPU-intensive Angelegenheit; man sollte sie daher nur selten einsetzen.
Zur Ausgabe von Bildern dienen die Funktionen ImageJPEG(), ImagePNG(), ImageWBMP() und ImageGIF(). Letztere ist wegen der Unisys-Patent-Streitigkeiten seit Version 1.6 der GD-Library nutzlos geworden, da kein GIF-Support mehr vorhanden ist. Die Ausgabefunktionen erwarten minimal einen Parameter: das auszugebende Bild-Handle. Zusätzlich kann man einen Dateinamen angeben, um statt einer Ausgabe an den Browser direkt in eine Datei zu schreiben. Will man sowohl an den Browser ausgeben, als auch die Datei im Filesystem ablegen, ruft man eine Ausgabefunktion zweimal hintereinander auf.
<?php
ImageJPEG($im2, $mypath . "/thumbs/$file.jpg", 80);
ImageJPEG($im2,"",80);
ImageDestroy($im);
ImageDestroy($im2);
?>
Ein einfaches Thumbnail-Script zeigt Listing 1. Es kann im HTML-Code der Gallerieseite mit <img src="thumbnail.php?i=test.jpg&w=neue_breite"> aufgerufen werden, wobei hier test.jpg durch die zu verkleinernde Datei und neue_breite durch die gewünschte Breite, beispielsweise 80 Pixel, zu ersetzen sind.
Listing 1: thumbnail.php
<?
if (!$i || !$w)
{ die(); }
header("Content-type: image/jpeg");
list($file, $suffix) = split("\.",$i);
$mypath = "/pfad/zu/grafiken/img/";
if(file_exists($mypath . $file . "_thumb_" . $w . ".jpg")) {
# Wenn Vorschaubild vorhanden, oeffnen und senden.
$fp = fopen($mypath . $file . "_thumb_" . $w . ".jpg", "rb");
fpassthru($fp);
fclose($fp);
} else {
# Bild erstellen
$im = ImageCreateFromJPEG($mypath . "$i");
$f = ImageSX($im)/$w;
$font = "/pfad/zu/fonts/verdana.ttf";
$breite_neu = $w;
$hoehe_neu = ImageSY($im)/$f;
$im2 = ImageCreateTrueColor($breite_neu,$hoehe_neu);
$black = ImageColorAllocate ($im2, 102, 102, 204);
$weiss = ImageColorAllocate ($im2, 255, 255, 255);
// ImageCopyResampled als Ersatz fuer ImageCopyResized:
// ImageCopyResized ($im2, $im, 0, 0, 0, 0,
// $breite_neu, $hoehe_neu, ImageSX($im), ImageSY($im));
ImageCopyResampled ($im2, $im, 0, 0, 0, 0,
$breite_neu, $hoehe_neu, ImageSX($im), ImageSY($im));
ImageTTFText($im2, 7, 0, 5, 10, $weiss, $font, $i);
ImageJPEG($im2, "img/" . $file . "_thumb_" . $w . ".jpg",80);
ImageJPEG($im2,"",80);
ImageDestroy($im);
ImageDestroy($im2);
}
?>
Grafische Ping-Statistik
Bilder zu verkleinern, ist nur eine der Aufgaben, für die eine serverseitige Grafikbibliothek nützlich sein kann. Auch bei anderen administrativen Funktionen ist eine Grafik bisweilen nützlich - speziell bei der Auslastungsanalyse. Mit MRTG existiert ein beliebtes Programm zur grafischen Auswertung von Netzwerkstatistiken, allerdings ist der Installationsaufwand bei einfachen Aufgaben nicht gerechtfertigt. Möchte der Systemadministrator nur eine einfache Ping-Statistik aufbereiten, dürfte er selten auf eine komplette MRTG- und SNMP-Installation zurückgreifen wollen. Hier empfiehlt sich wiederum PHP, da es auf vielen Webservern enthalten ist.
Folgende Lösung ist ein ‘Quick Hack’ und funktioniert, stellt aber alles andere als eine elegante Lösung dar. Als Datenquelle dient
ping -i 30 www.de-punkt.de \
> /opt/www.meinefirma.de/ping.txt
was so viel heißt wie ‘ein Paket alle 30 Sekunden verschicken und allen Output in die Datei ping.txt im Document Root von www.meinefirma.de.de schreiben’. Die resultierende Textdatei liest PHP über eine rekursive Funktion in ein eindimensionales Array, das die Ping-Antwortzeiten enthält. Nun legt der Programmierer auf die bekannte Art und Weise ein Image-Handle an (in diesem Fall ein True-Color-Bild mit 400 x 100) und initialisiert die drei Farben Schwarz, Weiß und Rot.
Mit der Funktion ImageFill() grundiert er das Diagramm in Weiß: ImageFill($im, 0, 0, $white); - $im ist das zu bearbeitende Image-Handle, die beiden Nullen bezeichnen die Anfangskoordinaten, an denen der ‘Flood Fill’ beginnen soll und $white ist die Füllfarbe. Nach der Grundierung macht sich ein schwarzer Rahmen als Randbegrenzer gut - er lässt sich leicht mit ImageRectangle() erstellen. Der Funktionsaufruf ImageRectangle($im, 0, 0, 399, 99, $black); zieht einen Rahmen in der Farbe $black (Schwarz) von den Startkoordinaten (0,0) zu (399,99) in dem durch $im bezeichneten Bild-Handle.
Nun zum eigentlichen Graph. Um die Intervalle für X- und Y-Achse festlegen zu können, muss der Entwickler den höchsten Ping-Wert sowie die Anzahl der Werte im Array ermitteln.
<?php
$max = max($values);
$fac = $max/100;
?>
legt das Maximum des Arrays $values und den Skalierungsfaktor für die Y-Achse fest. Wiederum ist die Bildhöhe von 100 Pixeln hardkodiert, hier bietet sich gegebenenfalls eine Variable ($height) an. Jetzt schickt der Programmierer das PHP-Script in eine Schleife, die für jeden Wert aus dem Array $values eine Linie von den Koordinaten des vorherigen Wertes zu denen des aktuellen zieht. Das Ziehen der Linie erledigt ImageLine(); ImageLine($im, $x_old, $y_old, $x, $y, $red) zieht eine Linie im Imagehandle $im von den Koordinaten ($x_old, $y_old) nach ($x, $y) in der Farbe $red. Die gesamte Schleife könnte etwa so aussehen:
<?php
foreach ($values as $value)
{
$x_old = $x;
$value_old = $value;
$x += round(400/count($values));
$y = abs($height-($value/$fac));
$y_old = abs($height-($value_old/$fac));
ImageLine($im, $x_old, $y_old, $x, $y, $red);
}
?>
Damit ist das einfache Ping-Diagramm fertig und kann durch ImageJPEG($im, 80) als JPEG in der Qualitätsstufe 80 Prozent an den Browser ausgegeben werden (siehe Listing 2).
Listing 2: Ping-Statistik
<?php
header("Content-Type: image/jpeg");
$fp = fopen ("ping.txt", "r");
$values = array();
$count = 0;
while (!feof($fp))
{
$line = fgets($fp, 1024);
$input = split("\ ", $line);
$packet = split("=", $input[7]);
$packet = $packet[1];
array_push ($values, $packet);
if ($count == 0)
array_pop($values);
$count++;
}
fclose($fp);
$im = ImageCreateTrueColor(400,100);
$black = ImageColorAllocate($im, 0, 0, 0);
$white = ImageColorAllocate($im, 255, 255, 255);
$red = ImageColorAllocate($im, 255, 0, 0);
$green = ImageColorAllocate($im, 0, 255, 0);
ImageFill($im, 0, 0, $white);
ImageRectangle($im, 0, 0, 399, 99, $black);
$max=max($values);
$fac=$max/100;
$x=0; $y=0;
foreach ($values as $value)
{
$x_old = $x;
$x += round(400/count($values));
$y = abs($height-($value/$fac));
$y_old = abs($height-($value_old/$fac));
ImageLine($im, $x_old, $y_old, $x, $y, $red);
$value_old = $value;
}
ImageJPEG($im, "",80);
?>
Balken und Linien: JPGraph
Für kurze Scripts ist ein schnell zusammengehacktes PHP-Script zwar genau richtig, wenn Kunden oder Entwickler aber höhere Ansprüche an Ausgabequalität und Komplexität des Diagramms stellen, bietet sich eine vorgefertigte Funktionensammlung an. JPGraph (siehe die ‘Online-Ressourcen’) ist eine hochwertige Klassensammlung, die eine große Anzahl an Diagrammtypen bietet. Neben Linien-, Balken- und Tortendiagrammen kann JPGraph Gantt-Graphen erstellen, die sich besonders gut für Groupware- und Kalenderfunktionen eignen. Zudem steht JPGraph unter der GPL.
Die Installion des Pakets ist denkbar einfach: eine gepackte Datei mit allen notwendigen Klassendateien und einer großen Menge an Beispielen zu jedem Diagrammtyp kann man von der in den ‘Online-Ressourcen’ genannten URL herunterladen - der Bequemlichkeit halber dürfte ein Systemadministrator sie in ein globales Verzeichnis für PHP entpacken, in dem die PHPLib zu finden ist. Alternativ lässt sich ein beliebiges anderes Verzeichnis wählen.
Benötigte Klassen für JPGraph lädt include() in das PHP-Script:
<?php
include ("jpgraph14/jpgraph.php");
include ("jpgraph14/jpgraph_bar.php");
include ("jpgraph14/jpgraph_line.php");
include ("jpgraph14/jpgraph_log.php");
?>
Im Anschluss daran initialisiert der Programmierer eine neue Instanz der Klasse Graph, das Diagrammobjekt, mit dem er fortan arbeiten will:
$graph = new Graph(500, 500, "auto");
Die beiden ersten Parameter legen Höhe und Breite des Diagramms, der letzte Parameter das Caching-Verhalten fest.
$graph->SetScale("textlin"); sorgt für die lineare Skalierung des Diagramms; andere Variablen erlauben Änderungen (fast) jedes Details. Den eigentlichen Graphen (oder Plot) instanziiert $plot = new BarPlot($values); - wobei $values das im vorigen Listing erstellte Array von Ping-Zeiten ist. Als Art des Diagramms dient hier ein BarPlot (Balkendiagramm, siehe gleichnamige Abbildung).
Somit ist der Graph im Grunde fertig - $graph->Add($plot) fügt den Plot dem Graphen hinzu (der mehrere Plots enthalten kann). Das Zeichnen selbst erledigt die Funktion Stroke(), der optional ein Dateiname übergeben werden kann.
$graph->Stroke("tmp.png") gibt den erstellten Graphen als PNG-Datei im Dateisystem aus. Der hier erstellte ist nicht so schön, wie er mit etwas zusätzlicher Konfiguration sein könnte, aber die umfangreichen Möglichkeiten der Anpassung von Diagrammen, die JPGraph bietet, würden den Rahmen dieses Artikels sprengen. Das fertige Script (inklusive eines Array aus Zufallszahlen als y-Werte) findet sich in Listing 2. Das Script enthält neben einem Bar-Plot noch einen Line-Plot mit anderen Werten und ist anti-aliased. Weitergehendes enthält Johan Perssons Website.
PHP eignet sich gut zur Erstellung dynamischer Grafiken, die die Qualität professioneller Grafikpakete erreichen können. Neben den ‘Bordmitteln’ der Grafikbibliothek GD kann der Anwender mit JPGraph auf ein ausgereiftes Paket zur Erstellung von Diagrammen zurückzugreifen, das für nahezu jede Anwendung einen passenden Diagrammtyp bietet.
Christopher Kunz
studiert Angewandte Informatik in Hannover und ist Geschäftsführer von de-punkt Webhosting.
iX-TRACT
- Nicht nur eine Datenbankanbindung ans Web lässt sich mit PHP realisieren; mit dynamischer Grafikerstellung können Entwickler Daten ausgeben.
- GD ist eine weit verbreitete Bibliothek für Grafik, nicht nur im PHP-Umfeld. JPGraph erleichtert das Generieren von Diagrammen.
- Mit wenigen Mitteln lassen sich beschriftete Thumbnails, BarCharts et cetera aus Daten erzeugen.
Listing 3: JPGraph verwenden
<?php
include ("jpgraph-1.5.3/src/jpgraph.php");
include ("jpgraph-1.5.3/src/jpgraph_bar.php");
include ("jpgraph-1.5.3/src/jpgraph_line.php");
$datay = array();
for ($i= 0; $i<10; $i++)
{
array_push($datay, rand(0,200)/100);
}
$datay2=array();
for ($i= 0; $i<10; $i++)
{
array_push($datay2, rand(0,100)/100);
}
$datax = array();
$targets = array();
$alts = array();
$graph = new Graph(400,100,0);
$graph->SetScale("textlin");
$bplot = new LinePlot($datay);
$graph->Add($bplot);
$bplot2 = new BarPlot($datay2);
$graph->Add($bplot2);
$graph->img->SetAntiAliasing();
$graph->Stroke("tmp.png");
echo "<img src='tmp.png'>";
?>
(hb)