3 /* *************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
14 * Ce fichier contient les fonctions utilisées
15 * par les fonctions-filtres de traitement d'image.
21 if (!defined('_ECRIRE_INC_VERSION')) return;
22 include_spip('inc/filtres'); // par precaution
23 include_spip('inc/filtres_images_mini'); // par precaution
26 * Transforme une couleur vectorielle R,G,B en hexa (par exemple pour usage css)
29 * Valeur du rouge de 0 à 255.
31 * Valeur du vert de 0 à 255.
33 * Valeur du bleu de 0 à 255.
35 * le code de la couleur en hexadécimal.
37 function _couleur_dec_to_hex($red, $green, $blue) {
39 $green = dechex($green);
40 $blue = dechex($blue);
42 if (strlen($red) == 1) $red = "0".$red;
43 if (strlen($green) == 1) $green = "0".$green;
44 if (strlen($blue) == 1) $blue = "0".$blue;
46 return "$red$green$blue";
50 * Transforme une couleur hexa en vectorielle R,G,B
52 * @param string $couleur
53 * Code couleur en hexa (#000000 à #FFFFFF).
55 * Un tableau des 3 éléments : rouge, vert, bleu.
57 function _couleur_hex_to_dec($couleur) {
58 $couleur = couleur_html_to_hex($couleur);
59 $couleur = preg_replace(",^#,","",$couleur);
60 $retour["red"] = hexdec(substr($couleur, 0, 2));
61 $retour["green"] = hexdec(substr($couleur, 2, 2));
62 $retour["blue"] = hexdec(substr($couleur, 4, 2));
69 * Donne un statut au fichier-image intermédiaire servant au traitement d'image
70 * selon qu'il doit être gravé (fichier .src) ou pas.
71 * Un appel php direct aux fonctions de filtre d'image produira ainsi une image
72 * permanente (gravée) ; un appel généré par le compilateur via
73 * filtrer('image_xx, ...) effacera automatiquement le fichier-image temporaire.
75 * @param bool|string $stat
76 * true, false ou le statut déjà défini si traitements enchaînés.
78 * true si il faut supprimer le fichier temporaire ; false sinon.
80 function statut_effacer_images_temporaires($stat){
81 static $statut = false; // par defaut on grave toute les images
82 if ($stat==='get') return $statut;
83 $statut = $stat?
true:false;
88 * Fonctions de traitement d'image
89 * Uniquement pour GD2.
92 * Un tag html <img src=... />.
93 * @param string $effet
94 * Les nom et paramètres de l'effet à apporter sur l'image
95 * (par exemple : reduire-300-200).
96 * @param bool|string $forcer_format
97 * Un nom d'extension spécifique demandé (par exemple : jpg, png, txt...
98 * par défaut false : GD se débrouille seule).
99 * @param array $fonction_creation
100 * Un tableau à 2 éléments. Le premier (string) indique le nom du
101 * filtre de traitement demandé (par exemple : image_reduire) ; le
102 * second (array) est lui-même un tableau reprenant la valeur de $img
103 * et chacun des paramètres passés au filtre.
104 * @param bool $find_in_path
105 * false (par défaut) indique que l'on travaille sur un fichier
106 * temporaire (.src) ; true, sur un fichier définitif déjà existant.
107 * @return bool|string|array
108 * false si pas de tag <img,
109 * si l'extension n'existe pas,
110 * si le fichier source n'existe pas,
111 * si les dimensions de la source ne sont pas accessibles,
112 * si le fichier temporaire n'existe pas,
113 * si la fonction _imagecreatefrom{extension} n'existe pas ;
114 * "" (chaîne vide) si le fichier source est distant et n'a pas
115 * réussi à être copié sur le serveur ;
116 * l'appel à la fonction pipeline image_preparer_filtre.
118 function _image_valeurs_trans($img, $effet, $forcer_format = false, $fonction_creation = NULL, $find_in_path = false) {
119 static $images_recalcul = array();
120 if (strlen($img)==0) return false;
122 $source = trim(extraire_attribut($img, 'src'));
123 if (strlen($source) < 1){
125 $img = "<img src='$source' />";
127 # gerer img src="data:....base64"
128 else if (preg_match('@^data:image/(jpe?g|png|gif);base64,(.*)$@isS', $source, $regs)) {
129 $local = sous_repertoire(_DIR_VAR
,'image-data').md5($regs[2]).'.'.str_replace('jpeg', 'jpg', $regs[1]);
130 if (!file_exists($local)) {
131 ecrire_fichier($local, base64_decode($regs[2]));
134 $img = inserer_attribut($img, 'src', $source);
135 # eviter les mauvaises surprises lors de conversions de format
136 $img = inserer_attribut($img, 'width', '');
137 $img = inserer_attribut($img, 'height', '');
140 // les protocoles web prennent au moins 3 lettres
141 if (preg_match(';^(\w{3,7}://);', $source)){
142 include_spip('inc/distant');
143 $fichier = _DIR_RACINE
. copie_locale($source);
144 if (!$fichier) return "";
146 // enlever le timestamp eventuel
147 $source=preg_replace(',[?][0-9]+$,','',$source);
151 $terminaison_dest = "";
152 if (preg_match(",\.(gif|jpe?g|png)($|[?]),i", $fichier, $regs)) {
153 $terminaison = strtolower($regs[1]);
154 $terminaison_dest = $terminaison;
156 if ($terminaison == "gif") $terminaison_dest = "png";
158 if ($forcer_format!==false) $terminaison_dest = $forcer_format;
160 if (!$terminaison_dest) return false;
162 $term_fonction = $terminaison;
163 if ($term_fonction == "jpg") $term_fonction = "jpeg";
165 $nom_fichier = substr($fichier, 0, strlen($fichier) - (strlen($terminaison) +
1));
166 $fichier_dest = $nom_fichier;
167 if (($find_in_path AND $f=find_in_path($fichier) AND $fichier=$f)
168 OR @file_exists
($f = $fichier)){
169 // on passe la balise img a taille image qui exraira les attributs si possible
170 // au lieu de faire un acces disque sur le fichier
171 list ($ret["hauteur"],$ret["largeur"]) = taille_image($find_in_path?
$f:$img);
172 $date_src = @filemtime
($f);
174 elseif (@file_exists
($f = "$fichier.src")
175 AND lire_fichier($f,$valeurs)
176 AND $valeurs=unserialize($valeurs)
177 AND isset($valeurs["hauteur_dest"])
178 AND isset($valeurs["largeur_dest"])) {
179 $ret["hauteur"] = $valeurs["hauteur_dest"];
180 $ret["largeur"] = $valeurs["largeur_dest"];
181 $date_src = $valeurs["date"];
183 // pas de fichier source par la
187 // pas de taille mesurable
188 if (!($ret["hauteur"] OR $ret["largeur"]))
193 // on a un dossier cache commun et un nom de fichier qui varie avec l'effet
194 // cas particulier de reduire :
195 // un cache par dimension, et le nom de fichier est conserve, suffixe par la dimension aussi
196 $cache = "cache-gd2";
197 if (substr($effet,0,7)=='reduire') {
198 list(,$maxWidth,$maxHeight) = explode('-',$effet);
199 list ($destWidth,$destHeight) = _image_ratio($ret['largeur'], $ret['hauteur'], $maxWidth, $maxHeight);
200 $ret['largeur_dest'] = $destWidth;
201 $ret['hauteur_dest'] = $destHeight;
202 $effet = "L{$destWidth}xH$destHeight";
203 $cache = "cache-vignettes";
204 $fichier_dest = basename($fichier_dest);
205 if (($ret['largeur']<=$maxWidth)&&($ret['hauteur']<=$maxHeight)){
206 // on garde la terminaison initiale car image simplement copiee
207 // et on postfixe son nom avec un md5 du path
208 $terminaison_dest = $terminaison;
209 $fichier_dest .= '-'.substr(md5("$fichier"),0,5);
212 $fichier_dest .= '-'.substr(md5("$fichier-$effet"),0,5);
213 $cache = sous_repertoire(_DIR_VAR
, $cache);
214 $cache = sous_repertoire($cache, $effet);
215 # cherche un cache existant
216 /*foreach (array('gif','jpg','png') as $fmt)
217 if (@file_exists($cache . $fichier_dest . '.' . $fmt)) {
218 $terminaison_dest = $fmt;
222 $fichier_dest = md5("$fichier-$effet");
223 $cache = sous_repertoire(_DIR_VAR
, $cache);
226 $fichier_dest = $cache . $fichier_dest . "." .$terminaison_dest;
228 $GLOBALS["images_calculees"][] = $fichier_dest;
231 // si recalcul des images demande, recalculer chaque image une fois
232 if (defined('_VAR_IMAGES') AND _VAR_IMAGES
AND !isset($images_recalcul[$fichier_dest])){
233 $images_recalcul[$fichier_dest] = true;
236 if (@file_exists
($f = $fichier_dest)){
237 if (filemtime($f)>=$date_src)
240 else if (@file_exists
($f = "$fichier_dest.src")
241 AND lire_fichier($f,$valeurs)
242 AND $valeurs=unserialize($valeurs)
243 AND $valeurs["date"]>=$date_src)
247 if (!@file_exists
($fichier)) {
248 if (!@file_exists
("$fichier.src")) {
249 spip_log("Image absente : $fichier");
252 # on reconstruit l'image source absente a partir de la chaine des .src
253 reconstruire_image_intermediaire($fichier);
258 spip_log("filtre image ".($fonction_creation?
reset($fonction_creation):'')."[$effet] sur $fichier","images"._LOG_DEBUG
);
260 // TODO: si une image png est nommee .jpg, le reconnaitre avec le bon $f
261 $ret["fonction_imagecreatefrom"] = "_imagecreatefrom".$term_fonction;
262 $ret["fichier"] = $fichier;
263 $ret["fonction_image"] = "_image_image".$terminaison_dest;
264 $ret["fichier_dest"] = $fichier_dest;
265 $ret["format_source"] = ($terminaison != 'jpeg' ?
$terminaison : 'jpg');
266 $ret["format_dest"] = $terminaison_dest;
267 $ret["date_src"] = $date_src;
268 $ret["creer"] = $creer;
269 $ret["class"] = extraire_attribut($img, 'class');
270 $ret["alt"] = extraire_attribut($img, 'alt');
271 $ret["style"] = extraire_attribut($img, 'style');
273 if ($fonction_creation){
274 $ret["reconstruction"] = $fonction_creation;
275 # ecrire ici comment creer le fichier, car il est pas sur qu'on l'ecrira reelement
276 # cas de image_reduire qui finalement ne reduit pas l'image source
277 # ca evite d'essayer de le creer au prochain hit si il n'est pas la
278 #ecrire_fichier($ret['fichier_dest'].'.src',serialize($ret),true);
281 $ret = pipeline('image_preparer_filtre',array(
285 'forcer_format'=>$forcer_format,
286 'fonction_creation'=>$fonction_creation,
287 'find_in_path'=>$find_in_path,
292 // une globale pour le debug en cas de crash memoire
293 $GLOBALS["derniere_image_calculee"] = $ret;
295 if (!function_exists($ret["fonction_imagecreatefrom"])) return false;
300 * Crée une image depuis un fichier ou une URL
301 * Utilise les fonctions spécifiques GD.
303 * @param string $filename
304 * Le path vers l'image à traiter (par exemple : IMG/distant/jpg/image.jpg
305 * ou local/cache-vignettes/L180xH51/image.jpg).
307 * Une ressource de type Image GD.
309 function _imagecreatefromjpeg($filename){
310 $img = @imagecreatefromjpeg
($filename);
312 spip_log("Erreur lecture imagecreatefromjpeg $filename",_LOG_CRITIQUE
);
313 erreur_squelette("Erreur lecture imagecreatefromjpeg $filename");
314 $img = imagecreate(10,10);
320 * Crée une image depuis un fichier ou une URL
321 * Utilise les fonctions spécifiques GD.
323 * @param string $filename
324 * Le path vers l'image à traiter (par exemple : IMG/distant/png/image.png
325 * ou local/cache-vignettes/L180xH51/image.png).
327 * Une ressource de type Image GD.
329 function _imagecreatefrompng($filename){
330 $img = @imagecreatefrompng
($filename);
332 spip_log("Erreur lecture imagecreatefrompng $filename",_LOG_CRITIQUE
);
333 erreur_squelette("Erreur lecture imagecreatefrompng $filename");
334 $img = imagecreate(10,10);
340 * Crée une image depuis un fichier ou une URL
341 * Utilise les fonctions spécifiques GD.
343 * @param string $filename
344 * Le path vers l'image à traiter (par exemple : IMG/distant/gif/image.gif
345 * ou local/cache-vignettes/L180xH51/image.gif).
347 * Une ressource de type Image GD.
349 function _imagecreatefromgif($filename){
350 $img = @imagecreatefromgif
($filename);
352 spip_log("Erreur lecture imagecreatefromgif $filename",_LOG_CRITIQUE
);
353 erreur_squelette("Erreur lecture imagecreatefromgif $filename");
354 $img = imagecreate(10,10);
360 * Affiche ou sauvegarde une image au format PNG
361 * Utilise les fonctions spécifiques GD.
363 * @param ressource $img
364 * Une ressource de type Image GD.
365 * @param string $fichier
366 * Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.png).
368 * false si l'image créée a une largeur nulle ou n'existe pas ;
369 * true si une image est bien retournée.
371 function _image_imagepng($img, $fichier) {
372 if (!function_exists('imagepng')) return false;
373 $tmp = $fichier.".tmp";
374 $ret = imagepng($img,$tmp);
376 if(file_exists($tmp)){
377 $taille_test = getimagesize($tmp);
378 if ($taille_test[0] < 1) return false;
380 spip_unlink($fichier); // le fichier peut deja exister
381 @rename
($tmp, $fichier);
388 * Affiche ou sauvegarde une image au format GIF
389 * Utilise les fonctions spécifiques GD.
391 * @param ressource $img
392 * Une ressource de type Image GD.
393 * @param string $fichier
394 * Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.gif).
396 * false si l'image créée a une largeur nulle ou n'existe pas;
397 * true si une image est bien retournée.
399 function _image_imagegif($img,$fichier) {
400 if (!function_exists('imagegif')) return false;
401 $tmp = $fichier.".tmp";
402 $ret = imagegif($img,$tmp);
404 if(file_exists($tmp)){
405 $taille_test = getimagesize($tmp);
406 if ($taille_test[0] < 1) return false;
408 spip_unlink($fichier); // le fichier peut deja exister
409 @rename
($tmp, $fichier);
416 * Affiche ou sauvegarde une image au format JPG
417 * Utilise les fonctions spécifiques GD.
419 * @param ressource $img
420 * Une ressource de type Image GD.
421 * @param string $fichier
422 * Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.jpg).
423 * @param int $qualite
424 * Le niveau de qualité du fichier résultant : de 0 (pire qualité, petit
425 * fichier) à 100 (meilleure qualité, gros fichier). Par défaut, prend la
426 * valeur (85) de la constante _IMG_GD_QUALITE (modifiable depuis
429 * false si l'image créée a une largeur nulle ou n'existe pas ;
430 * true si une image est bien retournée.
432 function _image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE
) {
433 if (!function_exists('imagejpeg')) return false;
434 $tmp = $fichier.".tmp";
435 $ret = imagejpeg($img,$tmp, $qualite);
437 if(file_exists($tmp)){
438 $taille_test = getimagesize($tmp);
439 if ($taille_test[0] < 1) return false;
441 spip_unlink($fichier); // le fichier peut deja exister
442 @rename
($tmp, $fichier);
449 * Crée un fichier-image au format ICO
450 * Utilise les fonctions de la classe phpthumb_functions.
452 * @param ressource $img
453 * Une ressource de type Image GD.
454 * @param string $fichier
455 * Le path vers l'image (ex : local/cache-vignettes/L180xH51/image.jpg).
457 * true si le fichier a bien été créé ; false sinon.
459 function _image_imageico($img, $fichier) {
460 $gd_image_array = array($img);
462 return ecrire_fichier($fichier, phpthumb_functions
::GD2ICOstring($gd_image_array));
466 * Finalise le traitement GD
467 * Crée un fichier_image temporaire .src ou vérifie que le fichier_image
468 * définitif a bien été créé.
470 * @param ressource $img
471 * Une ressource de type Image GD.
472 * @param array $valeurs
473 * Un tableau des informations (tailles, traitement, path...) accompagnant
475 * @param int $qualite
476 * N'est utilisé que pour les images jpg.
477 * Le niveau de qualité du fichier résultant : de 0 (pire qualité, petit
478 * fichier) à 100 (meilleure qualité, gros fichier). Par défaut, prend la
479 * valeur (85) de la constante _IMG_GD_QUALITE (modifiable depuis
482 * true si le traitement GD s'est bien finalisé ;
485 function _image_gd_output($img,$valeurs, $qualite=_IMG_GD_QUALITE
){
486 $fonction = "_image_image".$valeurs['format_dest'];
488 #un flag pour reperer les images gravees
490 !statut_effacer_images_temporaires('get') // si la fonction n'a pas ete activee, on grave tout
491 OR (@file_exists
($valeurs['fichier_dest']) AND !@file_exists
($valeurs['fichier_dest'].'.src'));
493 function_exists($fonction)
494 && ($ret = $fonction($img,$valeurs['fichier_dest'],$qualite)) # on a reussi a creer l'image
495 && isset($valeurs['reconstruction']) # et on sait comment la resonctruire le cas echeant
498 if (@file_exists
($valeurs['fichier_dest'])){
499 // dans tous les cas mettre a jour la taille de l'image finale
500 list ($valeurs["hauteur_dest"],$valeurs["largeur_dest"]) = taille_image($valeurs['fichier_dest']);
501 $valeurs['date'] = @filemtime
($valeurs['fichier_dest']); // pour la retrouver apres disparition
502 ecrire_fichier($valeurs['fichier_dest'].'.src',serialize($valeurs),true);
507 // http://doc.spip.org/@reconstruire_image_intermediaire
508 function reconstruire_image_intermediaire($fichier_manquant){
509 $reconstruire = array();
510 $fichier = $fichier_manquant;
512 !@file_exists
($fichier)
513 AND lire_fichier($src = "$fichier.src",$source)
514 AND $valeurs=unserialize($source)
515 AND ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
517 spip_unlink($src); // si jamais on a un timeout pendant la reconstruction, elle se fera naturellement au hit suivant
518 $reconstruire[] = $valeurs['reconstruction'];
520 while (count($reconstruire)){
521 $r = array_pop($reconstruire);
524 call_user_func_array($fonction, $args);
526 // cette image intermediaire est commune a plusieurs series de filtre, il faut la conserver
527 // mais l'on peut nettoyer les miettes de sa creation
528 ramasse_miettes($fichier_manquant);
531 // http://doc.spip.org/@ramasse_miettes
532 function ramasse_miettes($fichier){
533 if (!lire_fichier($src = "$fichier.src",$source)
534 OR !$valeurs=unserialize($source)) return;
535 spip_unlink($src); # on supprime la reference a sa source pour marquer cette image comme non intermediaire
537 ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
538 AND (substr($fichier,0,strlen(_DIR_VAR
))==_DIR_VAR
) # et est dans local
539 AND (lire_fichier($src = "$fichier.src",$source)) # le fichier a une source connue (c'est donc une image calculee intermediaire)
540 AND ($valeurs=unserialize($source)) # et valide
542 # on efface le fichier
543 spip_unlink($fichier);
544 # mais laisse le .src qui permet de savoir comment reconstruire l'image si besoin
549 // http://doc.spip.org/@image_graver
550 function image_graver($img){
551 // appeler le filtre post_image_filtrer qui permet de faire
552 // des traitements auto a la fin d'une serie de filtres
553 $img = pipeline('post_image_filtrer',$img);
555 $fichier = extraire_attribut($img, 'src');
556 if (($p=strpos($fichier,'?'))!==FALSE)
557 $fichier=substr($fichier,0,$p);
558 if (strlen($fichier) < 1)
560 # si jamais le fichier final n'a pas ete calcule car suppose temporaire
561 if (!@file_exists
($fichier))
562 reconstruire_image_intermediaire($fichier);
563 ramasse_miettes($fichier);
564 return $img; // on ne change rien
567 // Transforme une image a palette indexee (256 couleurs max) en "vraies" couleurs RGB
568 // Existe seulement pour compatibilite avec PHP < 5.5
569 // http://doc.spip.org/@imagepalettetotruecolor
570 if (!function_exists("imagepalettetotruecolor")) {
571 function imagepalettetotruecolor(&$img) {
572 if ($img AND !imageistruecolor($img) AND function_exists('imagecreatetruecolor')) {
575 $img1 = imagecreatetruecolor($w,$h);
576 //Conserver la transparence si possible
577 if(function_exists('ImageCopyResampled')) {
578 if (function_exists("imageAntiAlias")) imageAntiAlias($img1,true);
579 @imagealphablending
($img1, false);
580 @imagesavealpha
($img1,true);
581 @ImageCopyResampled
($img1, $img, 0, 0, 0, 0, $w, $h, $w, $h);
583 imagecopy($img1,$img,0,0,0,0,$w,$h);
591 // http://doc.spip.org/@image_tag_changer_taille
592 function _image_tag_changer_taille($tag,$width,$height,$style=false){
593 if ($style===false) $style = extraire_attribut($tag,'style');
594 // enlever le width et height du style
595 $style = preg_replace(",(^|;)\s*(width|height)\s*:\s*[^;]+,ims","",$style);
596 if ($style AND $style{0}==';') $style=substr($style,1);
597 // mettre des attributs de width et height sur les images,
598 // ca accelere le rendu du navigateur
599 // ca permet aux navigateurs de reserver la bonne taille
600 // quand on a desactive l'affichage des images.
601 $tag = inserer_attribut($tag,'width',$width);
602 $tag = inserer_attribut($tag,'height',$height);
604 // Ancien style inline pour IE6 mais qui casse la possibilité de surcharger en CSS
605 //$style = "height:".$height."px;width:".$width."px;".$style;
607 // attributs deprecies. Transformer en CSS
608 if ($espace = extraire_attribut($tag, 'hspace')){
609 $style = "margin:${espace}px;".$style;
610 $tag = inserer_attribut($tag,'hspace','');
612 $tag = inserer_attribut($tag,'style',$style, true, $style ?
false : true);
616 // function d'ecriture du de la balise img en sortie des filtre image
617 // reprend le tag initial et surcharge les tags modifies
618 function _image_ecrire_tag($valeurs,$surcharge=array()){
619 $valeurs = pipeline('image_ecrire_tag_preparer',$valeurs);
621 $tag = str_replace(">","/>",str_replace("/>",">",$valeurs['tag'])); // fermer les tags img pas bien fermes;
624 $style = $valeurs['style'];
625 if (isset($surcharge['style'])){
626 $style = $surcharge['style'];
627 unset($surcharge['style']);
630 // traiter specifiquement la largeur et la hauteur
631 $width = $valeurs['largeur'];
632 if (isset($surcharge['width'])){
633 $width = $surcharge['width'];
634 unset($surcharge['width']);
636 $height = $valeurs['hauteur'];
637 if (isset($surcharge['height'])){
638 $height = $surcharge['height'];
639 unset($surcharge['height']);
642 $tag = _image_tag_changer_taille($tag,$width,$height,$style);
643 // traiter specifiquement le src qui peut etre repris dans un onmouseout
644 // on remplace toute les ref a src dans le tag
645 $src = extraire_attribut($tag,'src');
646 if (isset($surcharge['src'])){
647 $tag = str_replace($src,$surcharge['src'],$tag);
648 // si il y a des & dans src, alors ils peuvent provenir d'un &
649 // pas garanti comme methode, mais mieux que rien
650 if (strpos($src,'&') !== false)
651 $tag = str_replace(str_replace("&","&",$src),$surcharge['src'],$tag);
652 $src = $surcharge['src'];
653 unset($surcharge['src']);
656 $class = $valeurs['class'];
657 if (isset($surcharge['class'])){
658 $class = $surcharge['class'];
659 unset($surcharge['class']);
662 $tag = inserer_attribut($tag,'class',$class);
664 if (count($surcharge))
665 foreach($surcharge as $attribut=>$valeur)
666 $tag = inserer_attribut($tag,$attribut,$valeur);
668 $tag = pipeline('image_ecrire_tag_finir',
671 'valeurs' => $valeurs,
672 'surcharge' => $surcharge,
681 function _image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process='AUTO', $force=false, $test_cache_only = false) {
682 // ordre de preference des formats graphiques pour creer les vignettes
683 // le premier format disponible, selon la methode demandee, est utilise
684 $image = $valeurs['fichier'];
685 $format = $valeurs['format_source'];
686 $destdir = dirname($valeurs['fichier_dest']);
687 $destfile = basename($valeurs['fichier_dest'],".".$valeurs["format_dest"]);
689 $format_sortie = $valeurs['format_dest'];
691 // liste des formats qu'on sait lire
692 $img = isset($GLOBALS['meta']['formats_graphiques'])
693 ?
(strpos($GLOBALS['meta']['formats_graphiques'], $format)!==false)
696 // si le doc n'est pas une image, refuser
697 if (!$force AND !$img) return;
698 $destination = "$destdir/$destfile";
702 if ($test_cache_only AND !$vignette) return;
704 // utiliser le cache ?
705 if (!$test_cache_only)
706 if ($force OR !$vignette OR (@filemtime
($vignette) < @filemtime
($image))) {
709 // calculer la taille
710 if (($srcWidth=$valeurs['largeur']) && ($srcHeight=$valeurs['hauteur'])){
711 if (!($destWidth=$valeurs['largeur_dest']) ||
!($destHeight=$valeurs['hauteur_dest']))
712 list ($destWidth,$destHeight) = _image_ratio($valeurs['largeur'], $valeurs['hauteur'], $maxWidth, $maxHeight);
714 elseif ($process == 'convert' OR $process == 'imagick') {
715 $destWidth = $maxWidth;
716 $destHeight = $maxHeight;
718 spip_log("echec $process sur $image");
722 // Si l'image est de la taille demandee (ou plus petite), simplement
725 AND $srcWidth <= $maxWidth AND $srcHeight <= $maxHeight) {
726 $vignette = $destination.'.'.$format;
727 @copy
($image, $vignette);
729 // imagemagick en ligne de commande
730 else if ($process == 'convert') {
731 define('_CONVERT_COMMAND', 'convert');
732 define ('_RESIZE_COMMAND', _CONVERT_COMMAND
.' -quality '._IMG_CONVERT_QUALITE
.' -resize %xx%y! %src %dest');
733 $vignette = $destination.".".$format_sortie;
734 $commande = str_replace(
735 array('%x', '%y', '%src', '%dest'),
739 escapeshellcmd($image),
740 escapeshellcmd($vignette)
745 if (!@file_exists
($vignette)) {
746 spip_log("echec convert sur $vignette");
747 return; // echec commande
751 // imagick (php4-imagemagick)
752 if ($process == 'imagick') {
753 $vignette = "$destination.".$format_sortie;
754 $handle = imagick_readimage($image);
755 imagick_resize($handle, $destWidth, $destHeight, IMAGICK_FILTER_LANCZOS
, _IMG_IMAGICK_QUALITE
/ 100);
756 imagick_write($handle, $vignette);
757 if (!@file_exists
($vignette)) {
758 spip_log("echec imagick sur $vignette");
764 if ($process == "netpbm") {
765 define('_PNMSCALE_COMMAND', 'pnmscale'); // chemin a changer dans mes_options
766 if (_PNMSCALE_COMMAND
== '') return;
767 $vignette = $destination.".".$format_sortie;
768 $pnmtojpeg_command = str_replace("pnmscale", "pnmtojpeg", _PNMSCALE_COMMAND
);
769 if ($format == "jpg") {
771 $jpegtopnm_command = str_replace("pnmscale", "jpegtopnm", _PNMSCALE_COMMAND
);
772 exec("$jpegtopnm_command $image | "._PNMSCALE_COMMAND
." -width $destWidth | $pnmtojpeg_command > $vignette");
773 if (!($s = @filesize
($vignette)))
774 spip_unlink($vignette);
775 if (!@file_exists
($vignette)) {
776 spip_log("echec netpbm-jpg sur $vignette");
779 } else if ($format == "gif") {
780 $giftopnm_command = str_replace("pnmscale", "giftopnm", _PNMSCALE_COMMAND
);
781 exec("$giftopnm_command $image | "._PNMSCALE_COMMAND
." -width $destWidth | $pnmtojpeg_command > $vignette");
782 if (!($s = @filesize
($vignette)))
783 spip_unlink($vignette);
784 if (!@file_exists
($vignette)) {
785 spip_log("echec netpbm-gif sur $vignette");
788 } else if ($format == "png") {
789 $pngtopnm_command = str_replace("pnmscale", "pngtopnm", _PNMSCALE_COMMAND
);
790 exec("$pngtopnm_command $image | "._PNMSCALE_COMMAND
." -width $destWidth | $pnmtojpeg_command > $vignette");
791 if (!($s = @filesize
($vignette)))
792 spip_unlink($vignette);
793 if (!@file_exists
($vignette)) {
794 spip_log("echec netpbm-png sur $vignette");
800 else if ($process == 'gd1' OR $process == 'gd2') {
801 if (_IMG_GD_MAX_PIXELS
&& $srcWidth*$srcHeight>_IMG_GD_MAX_PIXELS
){
802 spip_log("vignette gd1/gd2 impossible : ".$srcWidth*$srcHeight."pixels");
805 $destFormat = $format_sortie;
807 spip_log("pas de format pour $image");
811 $fonction_imagecreatefrom = $valeurs['fonction_imagecreatefrom'];
812 if (!function_exists($fonction_imagecreatefrom))
814 $srcImage = @$fonction_imagecreatefrom($image);
816 spip_log("echec gd1/gd2");
820 // Initialisation de l'image destination
821 if ($process == 'gd2' AND $destFormat != "gif")
822 $destImage = ImageCreateTrueColor($destWidth, $destHeight);
824 $destImage = ImageCreate($destWidth, $destHeight);
826 // Recopie de l'image d'origine avec adaptation de la taille
828 if (($process == 'gd2') AND function_exists('ImageCopyResampled')) {
829 if ($format == "gif") {
830 // Si un GIF est transparent,
831 // fabriquer un PNG transparent
832 $transp = imagecolortransparent($srcImage);
833 if ($transp > 0) $destFormat = "png";
835 if ($destFormat == "png") {
836 // Conserver la transparence
837 if (function_exists("imageAntiAlias")) imageAntiAlias($destImage,true);
838 @imagealphablending
($destImage, false);
839 @imagesavealpha
($destImage,true);
841 $ok = @ImageCopyResampled
($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
844 $ok = ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
846 // Sauvegarde de l'image destination
847 $valeurs['fichier_dest'] = $vignette = "$destination.$destFormat";
848 $valeurs['format_dest'] = $format = $destFormat;
849 _image_gd_output($destImage,$valeurs);
852 ImageDestroy($srcImage);
853 ImageDestroy($destImage);
856 $size = @getimagesize
($vignette);
857 // Gaffe: en safe mode, pas d'acces a la vignette,
858 // donc risque de balancer "width='0'", ce qui masque l'image sous MSIE
859 if ($size[0] < 1) $size[0] = $destWidth;
860 if ($size[1] < 1) $size[1] = $destHeight;
862 $retour['width'] = $largeur = $size[0];
863 $retour['height'] = $hauteur = $size[1];
865 $retour['fichier'] = $vignette;
866 $retour['format'] = $format;
867 $retour['date'] = @filemtime
($vignette);
874 // http://doc.spip.org/@image_ratio
875 function _image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
876 $ratioWidth = $srcWidth/$maxWidth;
877 $ratioHeight = $srcHeight/$maxHeight;
879 if ($ratioWidth <=1 AND $ratioHeight <=1) {
880 $destWidth = $srcWidth;
881 $destHeight = $srcHeight;
882 } else if ($ratioWidth < $ratioHeight) {
883 $destWidth = $srcWidth/$ratioHeight;
884 $destHeight = $maxHeight;
887 $destWidth = $maxWidth;
888 $destHeight = $srcHeight/$ratioWidth;
890 return array (ceil($destWidth), ceil($destHeight),
891 max($ratioWidth,$ratioHeight));
894 // Calculer le ratio ajuste sur la plus petite dimension
895 // http://doc.spip.org/@ratio_passe_partout
896 function ratio_passe_partout ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
897 $ratioWidth = $srcWidth/$maxWidth;
898 $ratioHeight = $srcHeight/$maxHeight;
900 if ($ratioWidth <=1 AND $ratioHeight <=1) {
901 $destWidth = $srcWidth;
902 $destHeight = $srcHeight;
903 } else if ($ratioWidth > $ratioHeight) {
904 $destWidth = $srcWidth/$ratioHeight;
905 $destHeight = $maxHeight;
908 $destWidth = $maxWidth;
909 $destHeight = $srcHeight/$ratioWidth;
911 return array (ceil($destWidth), ceil($destHeight),
912 min($ratioWidth,$ratioHeight));
915 // http://doc.spip.org/@process_image_reduire
916 function process_image_reduire($fonction,$img,$taille,$taille_y,$force,$cherche_image,$process){
918 if (($process == 'AUTO') AND isset($GLOBALS['meta']['image_process']))
919 $process = $GLOBALS['meta']['image_process'];
920 # determiner le format de sortie
921 $format_sortie = false; // le choix par defaut sera bon
922 if ($process == "netpbm") $format_sortie = "jpg";
923 else if ($process == 'gd1' OR $process == 'gd2') {
924 $image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);
926 // on verifie que l'extension choisie est bonne (en principe oui)
927 $gd_formats = explode(',',$GLOBALS['meta']["gd_formats"]);
928 if (!in_array($image['format_dest'],$gd_formats)
929 OR ($image['format_dest']=='gif' AND !function_exists('ImageGif'))
931 if ($image['format_source'] == 'jpg')
932 $formats_sortie = array('jpg','png','gif');
933 else // les gif sont passes en png preferentiellement pour etre homogene aux autres filtres images
934 $formats_sortie = array('png','jpg','gif');
935 // Choisir le format destination
936 // - on sauve de preference en JPEG (meilleure compression)
937 // - pour le GIF : les GD recentes peuvent le lire mais pas l'ecrire
938 # bug : gd_formats contient la liste des fichiers qu'on sait *lire*,
941 foreach ($formats_sortie as $fmt) {
942 if (in_array($fmt, $gd_formats)) {
943 if ($fmt <> "gif" OR function_exists('ImageGif'))
944 $format_sortie = $fmt;
953 $image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);
955 if (!$image OR !$image['largeur'] OR !$image['hauteur']){
956 spip_log("image_reduire_src:pas de version locale de $img");
957 // on peut resizer en mode html si on dispose des elements
958 if ($srcw = extraire_attribut($img, 'width')
959 AND $srch = extraire_attribut($img, 'height')) {
960 list($w,$h) = _image_ratio($srcw, $srch, $taille, $taille_y);
961 return _image_tag_changer_taille($img,$w,$h);
963 // la on n'a pas d'infos sur l'image source... on refile le truc a css
964 // sous la forme style='max-width: NNpx;'
965 return inserer_attribut($img, 'style',
966 "max-width: ${taille}px; max-height: ${taille_y}px");
969 // si l'image est plus petite que la cible retourner une copie cachee de l'image
970 if (($image['largeur']<=$taille)&&($image['hauteur']<=$taille_y)){
971 if ($image['creer']){
972 @copy
($image['fichier'], $image['fichier_dest']);
974 return _image_ecrire_tag($image,array('src'=>$image['fichier_dest']));
977 if ($image['creer']==false && !$force)
978 return _image_ecrire_tag($image,array('src'=>$image['fichier_dest'],'width'=>$image['largeur_dest'],'height'=>$image['hauteur_dest']));
980 if (in_array($image["format_source"],array('jpg','gif','png'))){
981 $destWidth = $image['largeur_dest'];
982 $destHeight = $image['hauteur_dest'];
983 $logo = $image['fichier'];
984 $date = $image["date_src"];
985 $preview = _image_creer_vignette($image, $taille, $taille_y,$process,$force);
987 if ($preview && $preview['fichier']) {
988 $logo = $preview['fichier'];
989 $destWidth = $preview['width'];
990 $destHeight = $preview['height'];
991 $date = $preview['date'];
993 // dans l'espace prive mettre un timestamp sur l'adresse
994 // de l'image, de facon a tromper le cache du navigateur
995 // quand on fait supprimer/reuploader un logo
996 // (pas de filemtime si SAFE MODE)
997 $date = test_espace_prive() ?
('?'.$date) : '';
998 return _image_ecrire_tag($image,array('src'=>"$logo$date",'width'=>$destWidth,'height'=>$destHeight));
1001 # SVG par exemple ? BMP, tiff ... les redacteurs osent tout!
1006 * Produire des fichiers au format .ico
1007 * avec du code recupere de :
1008 * phpThumb() by James Heinrich <info@silisoftware.com>
1009 * available at http://phpthumb.sourceforge.net
1011 * Class phpthumb_functions
1013 class phpthumb_functions
{
1016 * @param ressource $img
1019 * @return array|bool
1021 public static function GetPixelColor(&$img, $x, $y) {
1022 if (!is_resource($img)) {
1025 return @ImageColorsForIndex
($img, @ImageColorAt
($img, $x, $y));
1029 * @param int $number
1030 * @param int $minbytes
1033 public static function LittleEndian2String($number, $minbytes=1) {
1035 while ($number > 0) {
1036 $intstring = $intstring.chr($number & 255);
1039 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT
);
1043 * @param array $gd_image_array
1046 public static function GD2ICOstring(&$gd_image_array) {
1047 foreach ($gd_image_array as $key => $gd_image) {
1049 $ImageWidths[$key] = ImageSX($gd_image);
1050 $ImageHeights[$key] = ImageSY($gd_image);
1051 $bpp[$key] = ImageIsTrueColor($gd_image) ?
32 : 24;
1052 $totalcolors[$key] = ImageColorsTotal($gd_image);
1055 for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) {
1056 for ($x = 0; $x < $ImageWidths[$key]; $x++
) {
1057 $argb = phpthumb_functions
::GetPixelColor($gd_image, $x, $y);
1058 $a = round(255 * ((127 - $argb['alpha']) / 127));
1060 $g = $argb['green'];
1063 if ($bpp[$key] == 32) {
1064 $icXOR[$key] .= chr($b).chr($g).chr($r).chr($a);
1065 } elseif ($bpp[$key] == 24) {
1066 $icXOR[$key] .= chr($b).chr($g).chr($r);
1070 @$icANDmask[$key][$y] .= '1';
1072 @$icANDmask[$key][$y] .= '0';
1075 // mask bits are 32-bit aligned per scanline
1076 while (strlen($icANDmask[$key][$y]) %
32) {
1077 $icANDmask[$key][$y] .= '0';
1081 foreach ($icANDmask[$key] as $y => $scanlinemaskbits) {
1082 for ($i = 0; $i < strlen($scanlinemaskbits); $i +
= 8) {
1083 $icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT
)));
1089 foreach ($gd_image_array as $key => $gd_image) {
1090 $biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);
1092 // BITMAPINFOHEADER - 40 bytes
1093 $BitmapInfoHeader[$key] = '';
1094 $BitmapInfoHeader[$key] .= "\x28\x00\x00\x00"; // DWORD biSize;
1095 $BitmapInfoHeader[$key] .= phpthumb_functions
::LittleEndian2String($ImageWidths[$key], 4); // LONG biWidth;
1096 // The biHeight member specifies the combined
1097 // height of the XOR and AND masks.
1098 $BitmapInfoHeader[$key] .= phpthumb_functions
::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG biHeight;
1099 $BitmapInfoHeader[$key] .= "\x01\x00"; // WORD biPlanes;
1100 $BitmapInfoHeader[$key] .= chr($bpp[$key])."\x00"; // wBitCount;
1101 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biCompression;
1102 $BitmapInfoHeader[$key] .= phpthumb_functions
::LittleEndian2String($biSizeImage, 4); // DWORD biSizeImage;
1103 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // LONG biXPelsPerMeter;
1104 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // LONG biYPelsPerMeter;
1105 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biClrUsed;
1106 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biClrImportant;
1110 $icondata = "\x00\x00"; // idReserved; // Reserved (must be 0)
1111 $icondata .= "\x01\x00"; // idType; // Resource Type (1 for icons)
1112 $icondata .= phpthumb_functions
::LittleEndian2String(count($gd_image_array), 2); // idCount; // How many images?
1114 $dwImageOffset = 6 +
(count($gd_image_array) * 16);
1115 foreach ($gd_image_array as $key => $gd_image) {
1116 // ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
1118 $icondata .= chr($ImageWidths[$key]); // bWidth; // Width, in pixels, of the image
1119 $icondata .= chr($ImageHeights[$key]); // bHeight; // Height, in pixels, of the image
1120 $icondata .= chr($totalcolors[$key]); // bColorCount; // Number of colors in image (0 if >=8bpp)
1121 $icondata .= "\x00"; // bReserved; // Reserved ( must be 0)
1123 $icondata .= "\x01\x00"; // wPlanes; // Color Planes
1124 $icondata .= chr($bpp[$key])."\x00"; // wBitCount; // Bits per pixel
1126 $dwBytesInRes = 40 +
strlen($icXOR[$key]) +
strlen($icAND[$key]);
1127 $icondata .= phpthumb_functions
::LittleEndian2String($dwBytesInRes, 4); // dwBytesInRes; // How many bytes in this resource?
1129 $icondata .= phpthumb_functions
::LittleEndian2String($dwImageOffset, 4); // dwImageOffset; // Where in the file is this image?
1130 $dwImageOffset +
= strlen($BitmapInfoHeader[$key]);
1131 $dwImageOffset +
= strlen($icXOR[$key]);
1132 $dwImageOffset +
= strlen($icAND[$key]);
1135 foreach ($gd_image_array as $key => $gd_image) {
1136 $icondata .= $BitmapInfoHeader[$key];
1137 $icondata .= $icXOR[$key];
1138 $icondata .= $icAND[$key];