Générer et manipuler des images dynamiquement avec GD (2/2)
Transcription
Générer et manipuler des images dynamiquement avec GD (2/2)
Pour les débutants Générer et manipuler des images dynamiquement avec GD (2/2) La bibliothèque GD permet de manipuler et générer dynamiquement des images PNG, JPEG ou GIF, depuis un script PHP. Il est ainsi possible d’intégrer dans une application web des graphiques dont les données évoluent automatiquement au cours du temps. Cet article explique : Ce qu’il faut savoir : • Comment créer un graphique. • Les avantages et inconvénients de la génération de graphiques côté serveur avec GD. • Bases de PHP. L a bibliothèque graphique open source GD génère des images au format PNG, JPEG ou GIF. Elle permet de créer dynamiquement des graphiques, depuis un script PHP, à partir de données extraites d'une base de données ou d'un fichier. Ceci permet de proposer aux internautes des graphiques dont les données évoluent automatiquement au cours du temps : courbes journalières des températures minimales et maximales pour une ville, statistiques de consultation d'un site web, cours de la bourse, etc. Générer automatiquement un graphique rend possible la prise en compte des préférences de l'utilisateur : période d'intérêt, type de graphique souhaité, thème de couleurs, … L'article précédent a expliqué comment installer GD et comment redimensionner une image existante ou créer une image à partir de primitives graphiques (cercle, ellipse, arc, point, segment, rectangle, polygone). Cet article explique comment dessiner un graphique à partir de ces primitives et comment y ajouter un titre, des axes, des étiquettes et une légende. La dernière partie de l'article présente les avantages et inconvénients de GD, afin de vous permettre de déterminer si cette bibliothèque est adaptée à vos projets. Les critères utilisés ont été présentés dans le premier article de cette série sur la génération dynamique de graphiques (février 2012). 1 Créer un graphique Pour créer un graphique, il faut suivre les étapes suivantes : • créer le canevas avec la fonction imagecreate ou imagecreatetruecolor ; • créer le graphique à partir de primitives : un histogramme est dessiné à partir de rectangles, une courbe est représentée par un ensemble de segments, un diagramme à secteurs est composé d'arcs de cercle ; • ajouter des titres, étiquettes ou légendes ; • envoyer au navigateur le type MIME de l'image avec la fonction header et envoyer l'image avec la fonction imagepng, imagejpeg ou imagegif, en fonction de son format ; • nettoyer la mémoire avec la fonction imagedestroy ; • intégrer le graphique dans une page web avec l'élément HTML img. Attention, lors de la création du graphique, il faut veiller à ce qu'aucun texte ne soit envoyé avant l'image, sinon celle-ci ne pourra pas être affichée. La création du canevas, l'utilisation des primitives de base, l'envoi du graphique au navigateur, la libération de la mémoire, ainsi que l'inclusion du graphique dans une page HTML ont été présentés dans le numéro précédent de ce même magazine. Après avoir expliqué les différents types de police supportés par GD et la manière «d’insérer des textes 4/2012 Bibliothèque GD dans une image, nous verrons comment dessiner des graphiques. Textes GD permet d'écrire des textes en utilisant des polices matricielles prédéfinies ou des polices vectorielles. Les polices matricielles représentent chaque caractère par un tableau de points, tandis que les polices vectorielles représentent les caractères par des segments et des arcs. Polices matricielles GD permet d'écrire un texte avec une des cinq polices matricielles prédéfinies : FontTiny, FontSmall, FontMediumBold, FontLarge et FontGiant. Elles sont représentées par des entiers de 1 à 5. La fonction imagestring dessine une chaîne de caractères horizontalement dans l'image. Elle prend en argument la ressource d'image, le numéro de police, les coordonnées du point haut gauche, la chaîne à écrire et la couleur du texte. L'instruction suivante écrit la chaîne bonjour, elle est affichée à l'origine du repère : Deux fonctions aident à positionner le texte dans l'image : imagefontheight et imagefontwidth. Elles prennent en paramètre un numéro de police et retournent respectivement le nombre de pixels en hauteur et en largeur d'un caractère de cette police. Ceci permet par exemple de centrer le texte bonjour dans l'image dont la largeur est définie par la variable $width : $chaine = "bonjour"; $taille = strlen($chaine); $largeur = imagefontwidth(4)*$taille; imagestring($im, 4, ($width-$largeur)/2, 100, $chaine, $c_texte); Polices vectorielles Il est possible d'utiliser des polices vectorielles FreeType, TrueType et PostScript de type 1. Pour ce faire, il faut que les bibliothèques et les polices utilisées soient disponibles sur le serveur. Exécutez le script suivant pour vérifier le support des polices vectorielles par la version de GD : imagestring($im, 2, 0, 0, "bonjour", $c_texte); <?php La fonction imagestringup attend les mêmes arguments que imagestring, elle affiche la chaîne verticalement. ?> header('Content-type:text/plain'); print_r(gd_info()); Figure 1. Système de coordonnées et changement de repère phpsolmag.org/fr 2 Pour les débutants Listing 1a. fonctions.php * @param $marge int - decalage a droite par rapport au cadre <?php * @param $x_orig int - abscisse du point d’origine du * Dessine les barres d’un histogramme * @param $y_orig int - ordonnee du point d’origine du * @param $data tableau - tableau de donnees * @param $c_grille int - identifiant de couleur de la repere /** repere * @param $im ressource - ressource de l’image * @param $x_orig int - abscisse du point d’origine du repere * @param $y_orig int - ordonnee du point d’origine du repere function dessineGrille($im, $height, $width, $pas, $marge, $x_orig, $y_orig, $c_grille){ * @param $lg_barre int - largeur des barres // grille horizontale * @param $c_barres int - identifiant de couleur des for ($i=0; $i < $height-50; $i += $pas){ barres */ imageline($im, $x_orig, $y_orig-$i, $width- function dessineBarre($im, $data, $x_orig, $y_orig, $lg_barre, $c_barres){ $taille = count($data); for ($i=0; $i<$taille; $i++){ imagefilledrectangle($im, $x_orig+($i*$lg_barre), } /** * Ajoute des etiquettes de donnees sur l’axe des abscisses * @param $im ressource - ressource de l’image $x_orig+(($i+1)*$lg_barre)-5, } * @param $x_orig int - abscisse du point d’origine du $y_orig, $c_barres); repere * @param $y_orig int - ordonnee du point d’origine du repere /** * Dessine les axes d’un repere * @param $c_texte int - identifiant de couleur du texte * @param $x_orig int - abscisse du point d’origine du * @param $pas int - pas entre chaque etiquette (par * @param $etiquettes tableau - libelles des etiquettes * @param $im ressource - ressource de l’image defaut 70px) repere * @param $y_orig int - ordonnee du point d’origine du * @param $taille_txt int - taille de la police (par * @param $width int - largeur du canevas */ repere * @param $marge int - decalage a droite par rapport au cadre $c_texte, $etiquettes, $pas=70, $i = 0; axes */ imagefttext($im, $taille_txt, 0, ($x_ orig)+($pas*$i), $y_orig+20, $c_ $marge, $c_bordure){ imageline($im, $x_orig, $y_orig, $width-$marge, // ordonnees $y_orig, $c_bordure); imageline($im, $x_orig, $y_orig, $x_orig, $marge, $c_bordure); } /** * Dessine les barres de repere horizontales * @param $im ressource - ressource de l’image * @param $height int - hauteur du canevas * @param $width int - largeur du canevas * @param $pas int - pas entre chaque ligne horizontale $taille_txt = 11){ foreach ($etiquettes as $titre){ function dessineAxe($im, $x_orig, $y_orig, $width, // abscisses defaut 11pt) function ajouteEtiquettes($im, $x_orig, $y_orig, * @param $c_bordure int - identifiant de couleur des 3 $marge, $y_orig-$i, $c_grille); } $y_orig-$data[$i], } grille */ } } $i++; texte, ‘Arial.ttf’, $titre); /** * Dessine une courbe a partir d’un tableau de points * @param $im ressource - ressource de l’image * @param $data tableau - donnees * @param $x_orig int - abscisse du point d’origine du repere * @param $y_orig int - ordonnee du point d’origine du repere 4/2012 Bibliothèque GD Listing 1b. fonctions.php } * @param $pas int - pas entre chaque point de la /* calculer coordonnees boite englobante du libelle courbe le plus long */ * @param $c_fig int - identifiant de couleur de la $coord = imageftbbox(10, 0, ‘Arial.ttf’, $txt); courbe $largeur = $coord[2]-$coord[0]+10; * @param $c_fond int - identifiant de couleur de fond $hauteur = ($coord[3]-$coord[5])*2; */ function dessineCourbe($im, $data, $x_orig, $y_orig, /* coordonnees du bloc legende */ $x_legend = $x_centre+$diametre; $pas, $c_fig, $c_fond=null){ $y_legend = $y_centre-$diametre/2; /* si le parametre de couleur de fond est donne, $decalage = 30; la courbe devra etre dessinee en $nb_col = count($couleurs); pointilles */ if (!is_null($c_fond)){ $c_bloc = imagecolorallocatealpha($im, 255, 255, $style = array($c_fig, $c_fig, $c_fig, $c_fond, 255, 60); $c_legend = imagecolorallocate($im, 0, 0, 0); $c_fond, $c_fond, $c_fond, $c_fond); imagefilledrectangle($im, $x_legend, $y_legend, imagesetstyle($im, $style); } } $max=$nb; $x_legend+$decalage+$largeur, $c_fig = IMG_COLOR_STYLED; $y_legend+$hauteur*$taille_tab+10, $c_bloc); $taille = count($data); /* remplir le bloc avec les libelles et couleurs for ($i=0 ; $i < $taille-2; $i++){ $i = 1; // dessiner les segments foreach ($legende as $titre){ $xp1 = $x_orig+($i*$pas); $xp2 = $x_orig+(($i+1)*$pas); imagefilledrectangle($im, $x_legend+5, $yp1 = $y_orig-$data[$i]; $y_legend + $hauteur*$i-10, $yp2 = $y_orig-$data[$i+1]; } } associees */ $x_legend+20, $y_legend + $hauteur*$i, imageline($im, $xp1, $yp1, $xp2, $yp2, $c_fig); $couleurs[($i-1)%$nb_col]); imagefttext($im, 10, 0, $x_legend + $decalage, $y_legend + $hauteur*$i, $c_legend, /** * Ajoute legende a cote d’un diagramme a secteurs * @param $im ressource - ressource de l’image * @param $legende tableau - libelles des legendes a afficher * @param $couleurs tableau – couleurs des secteurs * @param $x_centre int - abscisse du centre du diagramme * @param $y_centre int - ordonnee du centre du diagramme * @param $diametre int - diametre du diagramme * @param $taille_tab int - taille du tableau de donnees */ function ajouteLegende($im, $legende, $couleurs, $x_centre, $y_centre, $diametre, $taille_tab){ // rechercher le libelle le plus long $max=0; foreach ($legende as $titre){ $nb = strlen($titre); if ($nb > $max){ $txt = $titre; phpsolmag.org/fr } } $i++; ‘Arial.ttf’, $titre); /** * Dessine un diagramme a secteurs et insere une legende * @param $im ressource - ressource de l’image * @param $tab tableau – tableau associatif de donnees et etiquettes * @param $x_centre int - abscisse du centre du diagramme * @param $y_centre int - ordonnee du centre du diagramme * @param $diametre int - diametre du diagramme * @param $angle_deb int - angle de debut des secteurs */ function dessineSecteurs($im, $tab, $x_centre, $y_centre, $diametre, $angle_deb){ $data = array_values($tab); $legende = array_keys($tab); $taille_tab = count($data); // alloue couleur aux secteurs 4 Pour les débutants Listing 1c. fonctions.php $c_secteur1 = imagecolorallocate($im, 255, 40, 40); $c_secteur2 = imagecolorallocate($im, 255, 180, 180); $c_secteur3 = imagecolorallocate($im, 255, 120, 120); $c_secteur4 = imagecolorallocate($im, 170, 170, 170); $couleurs = array($c_secteur1, $c_secteur2, $c_secteur3, $c_secteur4); $nb_col = count($couleurs); for ($i=0; $i<$taille_tab; $i++){ // calculer angle $val = $data[$i]*360/100 + $angle_deb; // tracer le secteur imagefilledarc($im, $x_centre, $y_centre, $diametre, $diametre, $angle_deb, $val, $couleurs[$i%$nb_col], IMG_ARC_PIE); } $angle_deb = $val; ajouteLegende($im, $legende, $couleurs, $x_centre, $y_centre, $diametre, } $taille_tab); Si les polices FreeType sont supportées, la clé FreeType Support du tableau retourné par gd _ info comporte la valeur 1. La clé T1Lib Support indique si les polices PostScript de niveau 1 sont supportées. Si les polices vectorielles ne sont pas supportées, il faut tout d'abord vérifier la présence des bibliothèques sur le serveur web : FreeType pour les polices FreeType et TrueType, T1Lib pour les polices PostScript de niveau 1. Le support de FreeType2 est obtenu lors de la compilation de PHP en utilisant --with-freetypedir lors de la configuration, celui de Postscript avec --with-t1lib, le support TrueType avec --enable-gdnative-ttf. Figure 2. Diagramme en barre simple 5 La fonction à utiliser pour écrire du texte dans une image dépend du type de police souhaité : imagefttext pour les polices FreeType, imagettftext pour les polices TrueType et imagepstext pour les polices PostScript. Dans cet article nous utiliserons les polices Arial. ttf et Comic.ttf. Les fonctions imagefttext et imagettftext prennent en argument : la ressource d'image, la taille de police en points, l'angle de rotation (0 pour un texte horizontal), les coordonnées du point bas gauche du premier caractère de la chaîne, la couleur, le fichier de police et le texte à afficher. Par exemple, l'instruction suivante affiche le mot bonjour horizontalement en police Arial, 12 pt, à partir du point de coordonnées (20,50) : imagefttext($im, 12, 0, 20, 50, $c_texte, 'Arial.ttf', 'bonjour'); La boîte englobante de la police peut être obtenue avec les fonctions imageftbbox (FreeType) ou imagettfbbox (TrueType). Elles prennent en argument la taille, l'angle de rotation, le fichier de police et le texte à afficher. Elles retournent un tableau contenant les coordonnées du coin bas gauche, bas droit, haut droit et haut gauche. La fonction ajouteLegende du script fonctions.php utilise ces coordonnées pour calculer automatiquement les dimensions du bloc où insérer une légende. Créer un histogramme Un histogramme est un ensemble de rectangles alignés les uns à côté des autres, qu'ils soient représentés avec des aires proportionnelles à leurs effectifs ou avec des longueurs proportionnelles, comme dans le cas des diagrammes en barre. Pour les représenter sur un canevas, il existe deux fonctions : imagerectangle qui dessine le contour d'un rectangle, ou imagefilledrectangle qui dessine des rectangles de couleur. Ces deux fonctions prennent en argument : une ressource d'image renvoyée par une fonction de création d'image (imagecreate, …), les coordonnées du sommet haut et gauche, les coordonnées du coin bas et droit et un iden- Figure 3. Diagramme en barre avec axes et titres 4/2012 Bibliothèque GD tifiant de couleur renvoyé par imagecolorallocate. Il est important de se rappeler que le centre du système de coordonnées utilisé par GD se situe sur le coin haut et gauche du canevas. La Figure 1 décrit le système de coordonnées utilisé dans cet article. Le Listing 2 affiche un diagramme de quatre barres sur un fond blanc (Figure 2). Le cadre du canevas a Listing 2. histogramme.php (Figure 2) été délimité en noir pour bien être identifié. La fonction dessineBarre (Listing 1) parcourt un tableau de données et dessine autant de rectangles colorés qu'il y a de cases. Il est possible d'ajouter des axes et des étiquettes sur le diagramme (Figure 3). Pour ce faire, il faut les dessiner et les ajouter à l'image. Une grille horizontale est ajou$height = 200; $im = imagecreate($width, $height); <?php require ‘fonctions.php’; // alloue couleurs // tableau de donnees $c_barres = imagecolorallocate($im, 127, 127, 255); $data = array(135, 75, 107, 35); // creer image $c_fond = imagecolorallocate($im, 255, 255, 255); $c_bordure = imagecolorallocate($im, 0, 0, 0); $c_grille = imagecolorallocate($im, 190, 190, 190); $width = 400; // configure coordonnees, marges et pas $im = imagecreate($width, $height); $y_orig = 165; $height = 200; // allouer couleurs $c_fond = imagecolorallocate($im, 255, 255, 255); $x_orig = 50; $marge = 20; $pas = 25; $c_barres = imagecolorallocate($im, 127, 127, 255); // arriere plan blanc // dessiner rectangles // cadre noir $c_bordure = imagecolorallocate($im, 0, 0, 0); // arriere plan blanc imagefilledrectangle($im, 0, 0, $width, $height, // cadre noir $c_fond); imagerectangle($im, 0, 0, $width-1, $height-1, $c_bordure); // dessiner barres de l’histogramme dessineBarre($im, $data, 60, 165, 70, $c_barres); // envoyer image imagefilledrectangle($im, 0, 0, $width, $height, $c_fond); imagerectangle($im, 0, 0, $width-1, $height-1, $c_bordure); dessineGrille($im, $height, $width, $pas, $marge, $x_orig, $y_orig, $c_grille); dessineBarre($im, array_values($data), $x_orig+10, $y_orig, 70, $c_barres); dessineAxe($im, $x_orig, $y_orig, $width, $marge, $c_bordure); header(“Content-type: image/png”); //ajout titres et etiquettes imagedestroy($im); imagefttext($im, 12, 0, 150, 20, $c_bordure, imagepng($im); ?> // titre ‘Arial.ttf’, ‘France 2011-2012’); Listing 3. histogramme_legende.php (Figure 3) // etiquettes des ordonnees imagefttext($im, 10, 90, 30, 150, $c_bordure, ‘Comic.ttf’, ‘Taux de pollution’); <?php ajouteEtiquettes($im, $x_orig+32, $y_orig, $c_bordure, // tableau de donnees // envoie image require ‘fonctions.php’; $data = array(‘ete’=>135, ‘aut’=>75, ‘hiv’=>107, ‘pri’=>35); // cree image $width = 400; phpsolmag.org/fr array_keys($data)); header(“Content-type: image/png”); imagepng($im); imagedestroy($im); ?> 6 Pour les débutants tée dans le Listing 3 derrière les barres afin de pouvoir mieux quantifier leur hauteur au premier coup d'œil. Les appels à la fonction imageline, qui permet de tracer un segment entre deux points, affichent une série de barres grises séparées par un pas de 25 pixels. Il est important de dessiner les grilles avant d'ajouter les barres car sinon les grilles seront affichées devant les barres. La fonction dessineAxe a permis d'ajouter les axes. en argument la ressource d'image et l'épaisseur en pixels. Les instructions ci-après créent un rectangle dont les coordonnées des coins haut gauche et bas droit sont respectivement (50, 50) et (100, 150). L'épaisseur du trait du rectangle est de 5 pixels. Créer une courbe Dans la Figure 4, générée par le listing 4, l'axe des abscisses représente des données qualitatives ainsi la distance entre les points ne varie pas. La fonction dessineCourbe (Listing 1) trace les segments adjacents à partir des données contenues dans le tableau passé en paramètre. Le trait de la courbe bleue est continu tandis que celui de la courbe rouge est dessiné en pointillés. Les pointillés ne sont affichés que si l'image a été créée avec la fonction imagecreatetruecolor. Une courbe est représentée par un ensemble de segments adjacents. Ces segments sont dessinés grâce à la fonction imageline. Il est possible de modifier le style et l'épaisseur du trait des segments, polygones et rectangles avec les fonctions imagesetstyle et imagesetthickness. La fonction imagesetstyle prend en argument la ressource d'image et un tableau de couleurs qui définit la couleur d'une suite de pixels. Lorsqu'une fonction qui trace une ligne reçoit la valeur IMG_COLOR_STYLED comme couleur, cette suite de pixels est utilisée. Le code ci-après dessine un rectangle en pointillés (deux pixels de couleur rouge pâle, suivis par deux pixels de la couleur du fond) : $c_fond = imagecolorallocate($im, 255, 255, 255); $c_fig = imagecolorallocate($im, 255, 127, 127); $style = array($c_fig, $c_fig, $c_fond, $c_fond); imagesetstyle($im, $style); imagerectangle($im, 50, 10, 100, 150, IMG_COLOR_STYLED); La fonction imagesetthickness définit l'épaisseur du trait des lignes, polygones et rectangles. Elle prend imagesetthickness($im, 5); imagerectangle($im, 50, 50, 100, 150, $c_fig); Créer un diagramme à secteurs La fonction imagearc dessine une ellipse partielle, centrée au point de coordonnées cx, cy. Lorsque la hauteur et la largeur sont égales, c'est un arc de cercle qui est tracé. La fonction prend en argument la ressource d'image, cx, cy, la largeur et la hauteur de l'arc, l'angle de début et de fin et un identifiant de couleur. L'instruction suivante crée un arc de cercle centré au point (100, 40), de largeur 30 pixels, dessiné à partir de l'angle 0 et se terminant à 180 degrés dans le sens des aiguilles d'une montre : imagearc($im, 100, 40, 30, 30, 0, 180, $couleur); Figure 4. Courbes 7 4/2012 Bibliothèque GD Pour obtenir un arc de cercle rempli, plutôt que son contour, il faut utiliser la fonction imagefilledarc. Celle-ci prend les mêmes arguments que la fonction imagearc, plus un argument IMG _ ARC _ s qui fixe le style, avec s une valeur ou une combinaison de valeurs parmi : – trace la corde, c'est-à-dire un segment qui relie deux points du cercle : le point de départ de l'angle et le point d'arrivée ; un triangle est dessiné entre ces deux points et le centre (cx, cy) ; • PIE – trace l'arc de cercle, c'est-à-dire la portion de cercle qui relie les points de départ et d'arrivée de l'angle, c'est cet argument qu'il faut utiliser pour créer un diagramme à secteurs ; • NOFILL – trace uniquement l'arc de cercle, les points délimitant le segment sur le cercle ne sont pas connectés au centre ; • CHORD Listing 4. courbe.php (Figure 4) • – connecte les angles de début et de fin au centre. EDGED En combinant les deux dernières valeurs, il est possible de dessiner uniquement le contour des secteurs du diagramme. L'instruction ci-après dessine un secteur sans remplissage : imagefilledarc($im, 100, 100, 70, 70, 0, 45, $c_fig, IMG_ ARC_EDGED|IMG_ARC_NOFILL); Dans le Listing 1, la fonction dessineSecteurs calcule les angles de chaque secteur à partir d'un tableau de pourcentages passé en paramètre. Dans l'exemple du Listing 5 (Figure 5), ce tableau contient trois valeurs mais la fonction gère un nombre variable de données. Les couleurs sont elles aussi attribuées automatiquement. La fonction appelle la fonction ajouteLegende qui insère un bloc qui contient la légende du diagramme. $c_courbe2 = imagecolorallocate($im, 255, 0, 0); $c_bordure = imagecolorallocate($im, 0, 0, 0); <?php require ‘fonctions.php’; //taux pollution par mois : ozone, pic en micro grammes / m3 (entre 0 et 360 avec un seuil moyen a 180 et un seuil critique a 240 en moyenne horaire) // indice de pollution de l’ozone $ozone = array(‘jun’=>180,’jul’=>200,’aou’=>170,’sep’=> $c_grille = imagecolorallocate($im, 190, 190, 190); // arriere plan blanc imagefilledrectangle($im, 0, 0, $width, $height, // cadre noir $c_fond); imagerectangle($im, 0, 0, $width-1, $height-1, $c_bordure); 110,’oct’=>105,’nov’=>80,’dec’=>120 dessineGrille($im, $height, $width, $pas, $marge, ’avr’=>50,’mai’=>10); dessineCourbe($im, array_values($ozone), $x_orig, ,’jan’=>135,’fev’=>120,’mar’=>40, // indice de pollution au dioxyde de carbone $carbone = array(‘jun’=>160,’jul’=>140,’aou’=>155,’sep’ =>135,’oct’=>105,’nov’=>120,’dec’=> 155,’jan’=>125,’fev’=>80,’mar’=>80, ’avr’=>20,’mai’=>60); // cree image $width = 600; $height = 300; $x_orig, $y_orig, $c_grille); $y_orig, $pas, $c_courbe1); dessineCourbe($im, array_values($carbone), $x_orig, $y_orig, $pas, $c_courbe2, $c_fond); dessineAxe($im, $x_orig, $y_orig, $width, $marge, $c_bordure); // ajout titres et etiquettes imagefttext($im, 12, 0, 240, 40, $c_bordure, ‘Arial.ttf’, ‘France 2011-2012’); $im = imagecreate($width, $height); imagefttext($im, 10, 90, 30, 200, $c_bordure, // coord, marges et pas ajouteEtiquettes($im, $x_orig-5, $y_orig, $c_bordure, $x_orig = 50; $y_orig = 265; $marge = 20; $pas = 50; // alloue couleurs $c_fond = imagecolorallocate($im, 255, 255, 255); $c_courbe1 = imagecolorallocate($im, 127, 127, 255); phpsolmag.org/fr ‘Comic.ttf’, ‘Taux de pollution’); array_keys($ozone), $pas-5, 8); // envoi image header(“Content-type: image/png”); imagepng($im); imagedestroy($im); ?> 8 Pour les débutants Les dimensions du bloc sont calculées automatiquement en fonction du nombre de caractères du plus grand libellé et de la taille de police choisie pour l'affichage. Transparence La fonction imagecolortransparent définit la couleur transparente de l'image. Elle prend en argument la ressource d'image ainsi qu'une ressource de couleur ou -1, pour signifier qu'il n'y a pas de couleur transparente. Par exemple, les instructions ci-après indiquent que les pixels noirs de l'image seront transparents : $c_fond = imagecolorallocate($im, 0, 0, 0); imagecolortransparent($im, $c_fond); La fonction imagecolorallocatealpha définit une couleur - à partir de la combinaison des trois couleurs primaires rouge, vert et bleu - et fixe son niveau de translucidité. Elle prend en argument, la ressource d'image, les valeurs des composants rouge, vert et bleu et le niveau de transparence. Celui-ci est un entier variant de 0 (opaque) à 127 (totalement transparent). La ressource d'image doit provenir d'une fonction qui crée des images TrueColor. Pour que la transparence soit supportée, il faut générer l'image au format PNG ou GIF. Dans la Figure 5, le bloc de légende est rempli en blanc. Il apparaît jaune clair car un niveau 60 de transparence a été défini pour le canal alpha (opacité d'environ 50%). Avantages et inconvénients Le choix d'une bibliothèque graphique dépend d'une combinaison de critères, dont la pondération est fonc- tion du périmètre et des contraintes du projet. Cette section reprend les critères définis dans le premier article de cette série, elle donne les avantages et inconvénients de l'utilisation de cette bibliothèque graphique côté serveur. Pérennité GD est une bibliothèque open source, distribuée depuis environ dix-huit ans. Disponible pour de nombreux langages de programmation, elle est distribuée par défaut avec PHP. La bibliothèque est utilisée par de nombreuses bibliothèques graphiques gratuites (pChart 2, PHPlot, LibChart, ...) et commerciales (JpGraph). Les deux versions majeures de la bibliothèque sont stables et de nombreux tutoriaux sont disponibles, la documentation PHP décrit toutes les fonctions GD et les illustre par des exemples. Fonctionnalités offertes GD est une bibliothèque de dessin matriciel. Elle ne propose pas de graphiques prédéfinis mais permet de créer des graphiques à partir de primitives : points, segments, arcs, cercles et ellipses, rectangles, polygones. La bibliothèque gère les couleurs, la transparence et la translucidité (GD2), l'anti-crénelage (GD2), et le stockage d'images TrueColor ou aux couleurs indexées. Des textes et légendes peuvent être ajoutés grâce au support des polices vectorielles FreeType, TrueType et PostScript. Les graphiques ainsi produits sont des images matricielles au format PNG, JPEG ou GIF. Ces images générées dynamiquement sont statiques dans le navigateur, contrairement à celles produites par les bibliothèques graphiques côté client. Figure 5. Diagramme en secteurs et transparence 9 4/2012 Bibliothèque GD Développeur La bibliothèque GD (programmation procédurale) est assez facile à prendre en main, grâce aux exemples donnés dans la documentation PHP et aux nombreux tutoriaux. La principale difficulté lors d'une utilisation pour générer des graphiques vient du fait qu'il faut définir toutes les couleurs et tracer les graphiques au moyen de primitives graphiques (cercle, polygone, point, segment, …). Ceci engendre un grand nombre de lignes de code. Expérience utilisateur L'image produite aura un aspect plus ou moins professionnel, en fonction de la maîtrise des techniques graphiques par le programmeur (conception d'un graphique à partir de primitives, choix des thèmes de couleur dans le graphique, utilisation de l'anti-crénelage, palette de couleur versus TrueColor). GD génère des images matricielles aux formats PNG, JPEG et GIF. C'est-à-dire des images composées d'une matrice de pixels. Dans de telles images, un cercle d'un diagramme à secteurs est représenté par un ensemble de points. La qualité de l'image dépend de sa résolution (nombre de pixels), de la profon- deur des couleurs (nombre de bits codant la couleur par pixel) et de l'échelle à laquelle elle est affichée. Un fort agrandissement de l'image fait apparaître les pixels composant le cercle. Affichés à leur taille d'origine, les graphiques générés par les bibliothèques produisant des images matricielles, sont généralement de bonne qualité. L'image créée dynamiquement est statique, c'est-àdire que toutes les informations sont affichées sur le graphique qui est envoyé au navigateur. Il est impossible de se déplacer dans le graphique ou de zoomer sur une sous-partie pour en analyser les données. L'avantage est que ces informations sont affichées de manière identique par tous les navigateurs (images PNG, JPEG, GIF) et qu'aucune technologie particulière n'est requise côté client (pas d'activation de JavaScript, SVG, HTML5, …). Par contre, en fonction du nombre de données, le graphique sera plus ou moins lisible. Pour des données temporelles denses, une bibliothèque graphique côté client sera plus adaptée. Sécurité GD est une bibliothèque côté serveur, elle ne nécessite donc pas l'activation de JavaScript pour fonctionner. Listing 5. secteurs.php (Figure 5) <?php require ‘fonctions.php’; // pourcentage de ville au dessus du seuil de pollution $data = array(‘sous les seuils’=>35, ‘entre les seuils’=>45, ‘au dessus des seuils’=>20); // cree image $width = 450; $height = 200; $im = imagecreatetruecolor($width, $height); // couleurs $c_fond = imagecolorallocate($im, 255, 255, 180); $c_bordure = imagecolorallocate($im, 0, 0, 0); // arriere plan jaune imagefilledrectangle($im, 0, 0, $width, $height, $c_fond); // cadre noir imagerectangle($im, 0, 0, $width-1, $height-1, $c_bordure); dessineSecteurs($im, $data, 120, 100, 150, 10); // envoi image header(“Content-type: image/png”); imagepng($im); imagedestroy($im); ?> phpsolmag.org/fr 10 Pour les débutants La bibliothèque est intégrée dans la distribution PHP, elle est également disponible par défaut dans de nombreuses distributions linux. Ceci assure que les correctifs de sécurité seront appliqués. Consommation des ressources Tous les calculs et traitements sont réalisés sur le serveur, le navigateur quant à lui n'est chargé que de l'affichage de l'image finale au format PNG, JPEG ou GIF. La consommation de ressources dépend de la taille du canevas et du système de stockage et de représentation de la couleur utilisé. Utiliser une palette de couleurs indexées plutôt que trois octets par pixel (image TrueColor) divise environ par trois la taille de l'image non compressée. Ceci a un impact sur la mémoire utilisée lors de la création de l'image et sur la bande passante. Charger une image en mémoire pour la redimensionner, ou créer une image aux dimensions importantes en TrueColor, peut rapidement provoquer l'arrêt du script à cause d'un dépassement de la taille mémoire allouée par défaut dans le fichier php.ini. Une image TrueColor stocke la couleur du pixel sur trois octets et utilise un octet supplémentaire pour gérer la translucidité. Ouvrir une image de ce type de 5000 pixels de large par 5000 pixels de haut nécessite environ six fois la taille mémoire allouée par défaut dans les dernières versions de PHP. Si l'image est un carré rouge uniforme de 5000 pixels, elle occupera en mémoire environ 96 Mo avant d'être compressée à quelques dizaines d'octets. La taille mémoire maximale autorisée peut être modifiée dans la directive memory_limit du fichier de configuration php.ini. Il faut cependant noter que la manipulation de très grandes images peut saturer la mémoire du serveur si le script de manipulation d'images est exécuté par plusieurs utilisateurs simultanément sur de grosses images. Afin d'adapter l'affichage d'un graphique à une page web, il est cependant rare de générer des images dépassant 1000 pixels de large. Licence La bibliothèque est open source et distribuée gratuitement. Il est possible d'étendre et de redistribuer son code, à condition d'inclure la licence. Sur Internet • • • • • 11 http://www.php.net/manual/fr/book.image.php – Documentation PHP GD, http://www.boutell.com/gd/ – Bibliothèque GD, http://www.libgd.org/ – Bibliothèque GD (téléchargement, documentation), http://www.freetype.org/ - FreeType 2, ftp://sunsite.unc.edu/pub/Linux/libs/graphics – PostScript 1 t1lib. Aspect financier La bibliothèque graphique GD est gratuite quelle que soit son utilisation. Le principal coût dont il faut tenir compte, est l'investissement en temps pour apprendre à utiliser cette bibliothèque et créer et maintenir un code comportant plusieurs centaines de lignes, dès lors que les graphiques proposés sont personnalisés. Conclusion La bibliothèque graphique GD permet de créer et manipuler des images matricielles depuis un script PHP. L'installation de GD, la création et le redimensionnement d'images ont été présentés dans le précédent article de cette série. Cet article a expliqué comment générer des graphiques à partir de primitives. La bibliothèque est principalement utilisée pour redimensionner ou modifier des images. Savoir utiliser cette bibliothèque est essentiel, par exemple, pour créer dynamiquement des imagettes à partir d'une image en grand format, envoyée par un utilisateur d'une application web. Bien qu'il soit possible de créer des graphiques avec GD, le processus de création est assez long et nécessite un grand nombre de lignes de code. En effet, les graphiques doivent être produits manuellement à partir de primitives. Pour pallier ce problème, il existe sur le marché un panel de bibliothèques côté serveur qui sont des surcouches de GD. Elles définissent des fonctions de haut niveau qui permettent de créer en très peu de lignes de code les principaux graphiques (JpGraph, pChart 2, ...). Dans un prochain numéro de ce magazine, vous apprendrez à créer des graphiques avec la bibliothèque PHP pChart 2. Cilia Mauro, Magali Contensin Cilia Mauro est gestionnaire de bases de données et développeur d’applications web à Aix-Marseille Université. Elle enseigne les bases de données et PHP à l’université. Contact : [email protected] Magali Contensin est chef de projet en développement d’applications au CNRS. Elle enseigne depuis plus de dix ans le développement d’applications web à l’université et est l’auteur d’un livre et de nombreux articles sur le développement web en PHP. Contact : http://magali.contensin.online.fr 4/2012