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