Aller au menu Aller au contenu Plan du site accessibilité

P0de*fr

Introduction

Il peut être parfois intéressant de redimensionner une image avec PHP, dans le cas d'une galerie par exemple. Le site génerera automatiquement des vignettes à partir d'une image de grande définition, ce qui évite l'utilisation d'un logiciel de retouche d'image et supprime une étape qui peut être plus ou moins longue.
Nous allons voir dans ce tutoriel comment envoyer une image à l'aide d'un formulaire HTML, puis la traiter avec la librairie GD (version 2.0.1 minimum!) afin de générer un nombre de vignettes à définir.

Pour y parvenir, décomposons les différentes étapes :

1. Formulaire HTML pour envoyer l'image - up

Voici le code du formulaire HTML :

<form method="post" action="envoi.php" enctype="multipart/form-data">
Image à envoyer : <input type="file" name="image" /><input type="submit" value="Envoyer" />
</form>

Notez la présence de enctype, ce paramêtre est nécesaire lors de l'envoi d'un fichier sur le server. Une fois validé, le formulaire va rediriger sur la page envoi.php, dans laquelle on va traiter l'image reçue.

2. Récupérer les informations de l'image envoyée - up

Il est possible de récupérer plusieurs informations sur l'image qui vient d'être envoyée, comme son type, son nom, son poids. Pour ce faire, rien de plus simple :

  • Poids : $_FILES['image']['size']; (si le nom du champs du formulaire est image!)
  • Type : $_FILES['image']['type'];
  • Nom : $_FILES['image']['name'];

3. Effectuer un simple redimensionnement - up

Et bien jusque là c'était simple, on va voir que pour effectuer un redimensionnement... ça l'est toujours! On va commencer par redimensionner une image de 640px*480px en vignette de 64px*48px. Cette image proviendrait de l'envoi du formulaire précédent.

<?php

//il faut tout d'abord créer un identifiant de l'image originale qu'on va stocker dans une variable

$imageOriginale = ImageCreatefromjpeg($_FILES['image']['name']);

//Puis on créé une image vide en mode "true color" avec les dimensions de l'image finale,
//on sauvegarde son identifiant dans une autre variable.

$imageRedimensionnee = ImageCreateTrueColor(64,48);

//Ensuite on copie l'image de taille originale dans la nouvelle avec les dimensions voulues.
//A noter que cette fonction permet de faire pas mal de choses intéressantes, voir la documentation
// PHP pour plus d'informations!

ImageCopyResampled($imageRedimensionnee, $imageOriginale, 0 , 0 , 0 , 0,64,48,640,480);

//Enfin, on génère l'image redimensionnée sur le server, en précisant son nom et sa qualité (%).
Imagejpeg($imageRedimensionnee,"image_redimensionnee.jpg",80);
?>

Mais tout n'est pas si simple, imaginons que la taille d'origine de l'image varie, qu'on désire une vignette qui fasse 80px de côté mais sans qu'elle soit déformée, donc en gardant le ratio largeur/hauteur d'origine, et que le nom de la vignette reprenne le nom de l'image envoyée mais avec un suffixe indiquant sa qualité de vignette.

Dans l'ordre il faudra donc :

  • Récupérer le nom de l'image originale et définir celui de la vignette
  • Récupérer ses dimensions largeur et hauteur.
  • Définir les dimensions de la vignette.
  • Générer une vignette suffixée.

C'est parti.

<?php

//on récupère le nom de l'image originale.
$nomImageOriginale = $_FILES['image']['name'];
//et on créé le nom de l'image avec son suffixe _80
$nomSansExtension = explode(".",$nomImageOriginale);
$nomVignette = $nomSansExtension[0]."_80.jpg";

//on récupère ses dimensions
$tailleImageOriginale = getimagesize($imageOriginale);
$largeurImageOriginale = $tailleImageOriginale[0];
$hauteurImageOriginale = $tailleImageOriginale[1];

//on définit les dimensions de la vignette
$ratioVertical = 80 / $largeurImageOriginale;
$ratioHorizontal = 80 / $hauteurImageOriginale;
$ratio = $ratioVertical < $ratioHorizontal ? $ratioVertical : $ratioHorizontal;
$largeurVignette = round($largeurImageOriginale * $ratio);
$hauteurVignette = round($hauteurImageOriginale * $ratio);

//maintenant qu'on a toutes les informations, on génère l'image
$imageOriginale = ImageCreatefromjpeg(nomImageOriginale);
$imageRedimensionnee = ImageCreateTrueColor(largeurVignette,hauteurVignette);
ImageCopyResampled($imageRedimensionnee, $imageOriginale, 0 , 0 , 0 , 0,80,
80,$largeurImageOriginale,$hauteurImageOriginale);
Imagejpeg($imageRedimensionnee,$nomVignette,80);
?>

Quelques explications complémentaires sont, je pense, nécessaires.
Tout d'abord la fonction getimagesize() permet de stocker dans un tableau les longueur et largeur de l'image. Une fois qu'on possède ces informations, on vérifie quelle dimension entre la largeur et la longueur va contraindre la dimension finale de la vignette. Dans notre cas par exemple, l'image fait 640px de large pour 480px de haut, la largeur est donc plus importante. Si on veut contraindre la vignette a une taille maximale de 80px de côté tout en gardant son ratio, la largeur finale sera de 80px, et la hauteur sera de 60px.

Comment ce code arrive à ce résultat ?

$ratioVertical = 80 / $largeurImageOriginale;
$ratioHorizontal = 80 / $hauteurImageOriginale;

Grâce à ces deux lignes on obtient : ratioVertical = 0.125 et ratioHorizontal = 0.166. Le ratio à garder est le plus faible, afin que la dimension la plus importante soit contenue dans 80px.

$ratio = $ratioVertical < $ratioHorizontal ? $ratioVertical : $ratioHorizontal;
$largeurVignette = round($largeurImageOriginale * $ratio);
$hauteurVignette = round($hauteurImageOriginale * $ratio);

La première ligne compare les deux ratio et sauvegarde dans $ratio la plus petite des deux valeurs.
Pour ceux du fond qui ne connaîtraient pas l'opérateur ternaire "?", cette ligne est équivalente à :

if ($ratioVertical < $ratioHorizontal) {
$ratio = $ratioVertical;
} else {
$ratio = $ratioHorizontal; }

Puis les deux lignes suivantes calculent les dimensions de la vignette grâce au ratio. Dans notre cas toujours, avec un ratio de 0.125 on obtient : $largeurVignette = 80; $hauteurVignette = 60.

Le reste est connu.

4. Script complet de redimensionnement "en boucle" - up

On vient de voir l'essentiel de ce qu'il y avait à savoir concernant le redimensionnement d'images JPG avec GD, cependant j'irais un peu plus loin. Le script suivant livré brut de décoffrage avec quelques commentaires ajoute des fonctionnalités intéressantes :

  • Tester le format de l'image (JPG).
  • Créer plusieurs vignettes à partir de la même image, à l'aide d'une boucle.
  • Vérifier si l'image a bien besoin d'être redimensionnée, afin de ne pas l'étirer.

Voici le script complet avec le formulaire d'envoi.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
<title>Redimensionnement d'images JPG avec GD sous PHP par http://pode.fr</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="author" content="pode.fr" />
<style type="text/css">
#www_pode_fr {
font-family: "Trebuchet MS",verdana,helvetica,sans-serif;
font-size: .8em;
}

</style>
</head>
<body id="www_pode_fr">

<?php

if (!$_GET['send']) {
?>

<form id="form_envoi_image" action="redim.php?send=ok" method="post" enctype="multipart/form-data">
<fieldset id="selectionner_image">
<legend>Envoyer une image JPG :</legend>
<p><label for="image">Image à envoyer : </label><input type="file" name="image" id="image" /></p>
<p style="text-align:center"><input type="submit" value="Envoyer" /></p>
</fieldset>
</form>
<?

} else {

//on vérifie le type de l'image
$acceptedTypes = array('image/jpeg', 'image/jpg', 'image/pjpeg');
if (in_array($_FILES['image']['type'], $acceptedTypes)) {

echo "Le type de l'image est correct.<br />\n";

//on récupère le nom de l'image originale.
$nomImageOriginale = $_FILES['image']['name'];
$nomSansExtension = explode(".",$nomImageOriginale);
echo "\t\tNom de l'image originale : ".$nomImageOriginale."<br />\n";

//on récupère ses dimensions
$tailleImageOriginale = getimagesize($nomImageOriginale);
$largeurImageOriginale = $tailleImageOriginale[0];
$hauteurImageOriginale = $tailleImageOriginale[1];
echo "\t\tLargeur Image Originale : ".$largeurImageOriginale."px<br />\n";
echo "\t\tHauteur Image Originale : ".$hauteurImageOriginale."px<br />\n";

//on créé un tableau destiné à accueillir les dimensions des différentes vignettes
//forme : (largeur_img1,hauteur_img1,largeur_img2,hauteur_img2, ...,...largeur_imgX,hauteur_imgX);
$matrice_redimensionnement = array(150,150,100,80,50,30); //=> 3 vignettes : 150*150 - 100*80 - 50*30
$nombreVignettes = count ($matrice_redimensionnement) / 2;

for ($i = 0; $i < $nombreVignettes; $i++) {

//on récupère les dimensions Max désirées
$largeurVignetteMax = $matrice_redimensionnement[$i*2]; //contient la largeur en pixel
$hauteurVignetteMax = $matrice_redimensionnement[$i*2+1]; //contient la hauteur en pixel
//on vérifie que la vignette soit bien plus petite que l'image originale,
//sinon on duplique l'image originale avec le nouveau suffixe.
if ($largeurImageOriginale < $largeurVignetteMax && $hauteurImageOriginale < $hauteurVignetteMax) {

echo "\t\t<br /><br />==== Vignette n°".($i+1)." : ";
echo "$largeurVignetteMax."*".$hauteurVignetteMax." ====<br />\n";
echo "\t\tLargeur Vignette obtenue : ".$largeurVignette."px<br />\n";
echo "\t\tHauteur Vignette obtenue : ".$hauteurVignette."px<br />\n";
echo "\t\tL'image n'a pas été redimensionnée car la vignette est plus grande<br />\n";
$imageOriginale = ImageCreatefromjpeg($nomImageOriginale);
$nomSansExtension = explode(".",$nomImageOriginale);
$nomVignette = $nomSansExtension[0]."_".$largeurVignetteMax.".jpg";
ImageJPEG($imageOriginale,$nomVignette,100);

}
else
{

//on calcule le ratio
$ratioVertical = $largeurVignetteMax / $largeurImageOriginale;
$ratioHorizontal = $hauteurVignetteMax / $hauteurImageOriginale;
$ratio = $ratioVertical < $ratioHorizontal ? $ratioVertical : $ratioHorizontal;
$largeurVignette = round($largeurImageOriginale * $ratio);
$hauteurVignette = round($hauteurImageOriginale * $ratio);
echo "\t\t<br /><br />==== Vignette n°".($i+1)." : ";
echo "$largeurVignetteMax."*".$hauteurVignetteMax." ====<br />\n";
echo "\t\tLargeur Vignette obtenue : ".$largeurVignette."px<br />\n";
echo "\t\tHauteur Vignette obtenue : ".$hauteurVignette."px<br />\n";

//maintenant qu'on a toutes les informations, on génère l'image
$imageOriginale = ImageCreatefromjpeg($nomImageOriginale);
$imageRedimensionnee = ImageCreateTrueColor($largeurVignette,$hauteurVignette);
ImageCopyResampled($imageRedimensionnee, $imageOriginale, 0 , 0 , 0 , 0, $largeurVignette, $hauteurVignette, $largeurImageOriginale, $hauteurImageOriginale);

//on génère le nom de la vignette avec le suffixe correspondant à la largeur max désirée
$nomSansExtension = explode(".",$nomImageOriginale);
$nomVignette = $nomSansExtension[0]."_".$largeurVignetteMax.".jpg";

//et enfin on génère la vignette
if (Imagejpeg($imageRedimensionnee,$nomVignette,100)) {

echo "\t\timage générée avec succès!";
}
}
}
} else {
echo "\t\tErreur : Type de l'image incorrect.";
}
}

?>

</body>
</html>

Pour une meilleure lisibilité du code je vous conseille vivement de télécharger le tutoriel ici et de l'ouvrir dans un éditeur.

5. Informations complémentaires - up

Il peut être tentant d'envoyer plusieurs images à redimensionner en une fois, avec le même formulaire. Cependant à cause des limitations de l'HTML il est impossible d'envoyer plus d'un fichier, donc une image, à la fois. La solution que j'utilise dans ce cas est l'envoi des images originales sur le FTP. Je lance ensuite un script PHP sur le server qui scanne le répertoire qui les contient puis les redimensionne. Si cette solution vous intéresse, vous pouvez vous référer à mon tutoriel sur la manipulation de fichiers, qui explique par exemple comment effectuer un "scan" de répertoire.

Le truc magique avec ce tutoriel est qu'il fonctionne également pour les fichiers png, à condition d'utiliser les bonnes fonctions : imageCreateFromPNG(), imagePNG(). Cependant le GIF n'est plus supporté par la dernière version de GD pour des raisons de droits, mais bon le GIF c'est has been...

Je ne sais plus ce que je voulais dire d'autre.