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 if (!defined('_ECRIRE_INC_VERSION')) return;
15 include_spip('inc/filtres'); // par precaution
19 * Transforme une couleur vectorielle R,G,B en hexa
26 function _couleur_dec_to_hex($red, $green, $blue) {
28 $green = dechex($green);
29 $blue = dechex($blue);
31 if (strlen($red) == 1) $red = "0".$red;
32 if (strlen($green) == 1) $green = "0".$green;
33 if (strlen($blue) == 1) $blue = "0".$blue;
35 return "$red$green$blue";
39 * Transforme une couleur hexa en vectorielle R,G,B
41 * @param string $couleur
44 function _couleur_hex_to_dec($couleur) {
45 include_spip("inc/filtres_images_mini");
46 $couleur = couleur_html_to_hex($couleur);
47 $couleur = preg_replace(",^#,","",$couleur);
48 $retour["red"] = hexdec(substr($couleur, 0, 2));
49 $retour["green"] = hexdec(substr($couleur, 2, 2));
50 $retour["blue"] = hexdec(substr($couleur, 4, 2));
56 function statut_effacer_images_temporaires($stat){
57 static $statut = false; // par defaut on grave toute les images
58 // une constante a utiliser lorsqu'on a des filtres image_xxx qui ne produisent pas des images
59 if (defined('_CONSERVER_IMAGES_TEMPORAIRES'))
61 if ($stat==='get') return $statut;
62 $statut = $stat?
true:false;
65 // http://doc.spip.org/@cherche_image_nommee
66 function cherche_image_nommee($nom, $formats = array ('gif', 'jpg', 'png')) {
68 if (strncmp(_DIR_IMG
, $nom,$n=strlen(_DIR_IMG
))==0) {
69 $nom = substr($nom,$n);
70 } else if (strncmp(_DIR_IMG_PACK
, $nom,$n=strlen(_DIR_IMG_PACK
))==0) {
71 $nom = substr($nom,$n);
72 } else if (strncmp(_DIR_IMG_ICONE_DIST
, $nom,$n=strlen(_DIR_IMG_ICONES_DIST
))==0) {
73 $nom = substr($nom,$n);
75 $pos = strrpos($nom, "/");
77 $chemin = substr($nom, 0, $pos+
1);
78 $nom = substr($nom, $pos+
1);
84 while (list(, $format) = each($formats)) {
85 if (@file_exists
(_DIR_IMG
. "$chemin$nom.$format")){
86 return array((_DIR_IMG
. $chemin), $nom, $format);
87 } else if (@file_exists
(_DIR_IMG_PACK
. "$chemin$nom.$format")){
88 return array((_DIR_IMG_PACK
. $chemin), $nom, $format);
89 } else if (@file_exists
(_DIR_IMG_ICONES_DIST
. "$chemin$nom.$format")){
90 return array((_DIR_IMG_ICONES_DIST
. $chemin), $nom, $format);
95 // Fonctions de traitement d'image
96 // uniquement pour GD2
97 // http://doc.spip.org/@image_valeurs_trans
98 function _image_valeurs_trans($img, $effet, $forcer_format = false, $fonction_creation = NULL) {
99 static $images_recalcul = array();
100 if (strlen($img)==0) return false;
102 $source = trim(extraire_attribut($img, 'src'));
103 if (strlen($source) < 1){
105 $img = "<img src='$source' />";
107 # gerer img src="data:....base64"
108 else if (preg_match('@^data:image/(jpe?g|png|gif);base64,(.*)$@isS', $source, $regs)) {
109 $local = sous_repertoire(_DIR_VAR
,'image-data').md5($regs[2]).'.'.str_replace('jpeg', 'jpg', $regs[1]);
110 if (!file_exists($local)) {
111 ecrire_fichier($local, base64_decode($regs[2]));
114 $img = inserer_attribut($img, 'src', $source);
115 # eviter les mauvaises surprises lors de conversions de format
116 $img = inserer_attribut($img, 'width', '');
117 $img = inserer_attribut($img, 'height', '');
120 // les protocoles web prennent au moins 3 lettres
121 if (preg_match(';^(\w{3,7}://);', $source)){
122 include_spip('inc/distant');
123 $fichier = _DIR_RACINE
. copie_locale($source);
124 if (!$fichier) return "";
126 // enlever le timestamp eventuel
127 $source=preg_replace(',[?][0-9]+$,','',$source);
131 $terminaison_dest = "";
132 if (preg_match(",\.(gif|jpe?g|png)($|[?]),i", $fichier, $regs)) {
133 $terminaison = strtolower($regs[1]);
134 $terminaison_dest = $terminaison;
136 if ($terminaison == "gif") $terminaison_dest = "png";
138 if ($forcer_format!==false) $terminaison_dest = $forcer_format;
140 if (!$terminaison_dest) return false;
142 $term_fonction = $terminaison;
143 if ($term_fonction == "jpg") $term_fonction = "jpeg";
145 $nom_fichier = substr($fichier, 0, strlen($fichier) - (strlen($terminaison) +
1));
146 $fichier_dest = $nom_fichier;
148 if (@file_exists
($f = $fichier)){
149 list ($ret["hauteur"],$ret["largeur"]) = taille_image($img);
150 $date_src = filemtime($f);
152 elseif (@file_exists
($f = "$fichier.src")
153 AND lire_fichier($f,$valeurs)
154 AND $valeurs=unserialize($valeurs)
155 AND isset($valeurs["hauteur_dest"])
156 AND isset($valeurs["largeur_dest"])) {
157 $ret["hauteur"] = $valeurs["hauteur_dest"];
158 $ret["largeur"] = $valeurs["largeur_dest"];
159 $date_src = $valeurs["date"];
161 // pas de fichier source par la
165 // pas de taille mesurable
166 if (!($ret["hauteur"] OR $ret["largeur"]))
171 // on a un dossier cache commun et un nom de fichier qui varie avec l'effet
172 // cas particulier de reduire :
173 // un cache par dimension, et le nom de fichier est conserve, suffixe par la dimension aussi
174 $cache = "cache-gd2";
175 if (substr($effet,0,7)=='reduire') {
176 list(,$maxWidth,$maxHeight) = explode('-',$effet);
177 list ($destWidth,$destHeight) = _image_ratio($ret['largeur'], $ret['hauteur'], $maxWidth, $maxHeight);
178 $ret['largeur_dest'] = $destWidth;
179 $ret['hauteur_dest'] = $destHeight;
180 $effet = "L{$destWidth}xH$destHeight";
181 $cache = "cache-vignettes";
182 $fichier_dest = basename($fichier_dest);
183 if (($ret['largeur']<=$maxWidth)&&($ret['hauteur']<=$maxHeight)){
184 // on garde la terminaison initiale car image simplement copiee
185 // et on postfixe son nom avec un md5 du path
186 $terminaison_dest = $terminaison;
187 $fichier_dest .= '-'.substr(md5("$fichier"),0,5);
190 $fichier_dest .= '-'.substr(md5("$fichier-$effet"),0,5);
191 $cache = sous_repertoire(_DIR_VAR
, $cache);
192 $cache = sous_repertoire($cache, $effet);
193 # cherche un cache existant
194 /*foreach (array('gif','jpg','png') as $fmt)
195 if (@file_exists($cache . $fichier_dest . '.' . $fmt)) {
196 $terminaison_dest = $fmt;
200 $fichier_dest = md5("$fichier-$effet");
201 $cache = sous_repertoire(_DIR_VAR
, $cache);
204 $fichier_dest = $cache . $fichier_dest . "." .$terminaison_dest;
205 $GLOBALS["images_calculees"][] = $fichier_dest;
208 // si recalcul des images demande, recalculer chaque image une fois
209 if (isset($GLOBALS['var_images']) && $GLOBALS['var_images'] && !isset($images_recalcul[$fichier_dest])){
210 $images_recalcul[$fichier_dest] = true;
213 if (@file_exists
($f = $fichier_dest)){
214 if (filemtime($f)>=$date_src)
217 else if (@file_exists
($f = "$fichier_dest.src")
218 AND lire_fichier($f,$valeurs)
219 AND $valeurs=unserialize($valeurs)
220 AND $valeurs["date"]>=$date_src)
224 if (!@file_exists
($fichier)) {
225 if (!@file_exists
("$fichier.src")) {
226 spip_log("Image absente : $fichier");
229 # on reconstruit l'image source absente a partir de la chaine des .src
230 reconstruire_image_intermediaire($fichier);
233 // todo: si une image png est nommee .jpg, le reconnaitre avec le bon $f
234 $f = "imagecreatefrom".$term_fonction;
235 if (!function_exists($f)) return false;
236 $ret["fonction_imagecreatefrom"] = $f;
237 $ret["fichier"] = $fichier;
238 $ret["fonction_image"] = "_image_image".$terminaison_dest;
239 $ret["fichier_dest"] = $fichier_dest;
240 $ret["format_source"] = ($terminaison != 'jpeg' ?
$terminaison : 'jpg');
241 $ret["format_dest"] = $terminaison_dest;
242 $ret["date_src"] = $date_src;
243 $ret["creer"] = $creer;
244 $ret["class"] = extraire_attribut($img, 'class');
245 $ret["alt"] = extraire_attribut($img, 'alt');
246 $ret["style"] = extraire_attribut($img, 'style');
249 if ($fonction_creation){
250 $ret["reconstruction"] = $fonction_creation;
251 # ecrire ici comment creer le fichier, car il est pas sur qu'on l'ecrira reelement
252 # cas de image_reduire qui finalement ne reduit pas l'image source
253 # ca evite d'essayer de le creer au prochain hit si il n'est pas la
254 #ecrire_fichier($ret['fichier_dest'].'.src',serialize($ret),true);
259 // http://doc.spip.org/@image_imagepng
260 function _image_imagepng($img,$fichier) {
261 if (!function_exists('imagepng')) return false;
262 $tmp = $fichier.".tmp";
263 $ret = imagepng($img,$tmp);
265 $taille_test = getimagesize($tmp);
266 if ($taille_test[0] < 1) return false;
268 spip_unlink($fichier); // le fichier peut deja exister
269 @rename
($tmp, $fichier);
273 // http://doc.spip.org/@image_imagegif
274 function _image_imagegif($img,$fichier) {
275 if (!function_exists('imagegif')) return false;
276 $tmp = $fichier.".tmp";
277 $ret = imagegif($img,$tmp);
279 $taille_test = getimagesize($tmp);
280 if ($taille_test[0] < 1) return false;
283 spip_unlink($fichier); // le fichier peut deja exister
284 @rename
($tmp, $fichier);
287 // http://doc.spip.org/@image_imagejpg
288 function _image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE
) {
289 if (!function_exists('imagejpeg')) return false;
290 $tmp = $fichier.".tmp";
291 $ret = imagejpeg($img,$tmp, $qualite);
293 $taille_test = getimagesize($tmp);
294 if ($taille_test[0] < 1) return false;
296 spip_unlink($fichier); // le fichier peut deja exister
297 @rename
($tmp, $fichier);
300 // http://doc.spip.org/@image_imageico
301 function _image_imageico($img, $fichier) {
302 $gd_image_array = array($img);
304 return ecrire_fichier($fichier, phpthumb_functions
::GD2ICOstring($gd_image_array));
307 // $qualite est utilise pour la qualite de compression des jpeg
308 // http://doc.spip.org/@image_gd_output
309 function _image_gd_output($img,$valeurs, $qualite=_IMG_GD_QUALITE
){
310 $fonction = "_image_image".$valeurs['format_dest'];
312 #un flag pour reperer les images gravees
314 !statut_effacer_images_temporaires('get') // si la fonction n'a pas ete activee, on grave tout
315 OR (@file_exists
($valeurs['fichier_dest']) AND !@file_exists
($valeurs['fichier_dest'].'.src'));
317 function_exists($fonction)
318 && ($ret = $fonction($img,$valeurs['fichier_dest'],$qualite)) # on a reussi a creer l'image
319 && isset($valeurs['reconstruction']) # et on sait comment la resonctruire le cas echeant
322 if (@file_exists
($valeurs['fichier_dest'])){
323 // dans tous les cas mettre a jour la taille de l'image finale
324 list ($valeurs["hauteur_dest"],$valeurs["largeur_dest"]) = taille_image($valeurs['fichier_dest']);
325 $valeurs['date'] = filemtime($valeurs['fichier_dest']); // pour la retrouver apres disparition
326 ecrire_fichier($valeurs['fichier_dest'].'.src',serialize($valeurs),true);
332 // http://doc.spip.org/@reconstruire_image_intermediaire
333 function reconstruire_image_intermediaire($fichier_manquant){
334 $reconstruire = array();
335 $fichier = $fichier_manquant;
337 !@file_exists
($fichier)
338 AND lire_fichier($src = "$fichier.src",$source)
339 AND $valeurs=unserialize($source)
340 AND ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
342 spip_unlink($src); // si jamais on a un timeout pendant la reconstruction, elle se fera naturellement au hit suivant
343 $reconstruire[] = $valeurs['reconstruction'];
345 while (count($reconstruire)){
346 $r = array_pop($reconstruire);
349 call_user_func_array($fonction, $args);
351 // cette image intermediaire est commune a plusieurs series de filtre, il faut la conserver
352 // mais l'on peut nettoyer les miettes de sa creation
353 ramasse_miettes($fichier_manquant);
356 // http://doc.spip.org/@ramasse_miettes
357 function ramasse_miettes($fichier){
358 if (!lire_fichier($src = "$fichier.src",$source)
359 OR !$valeurs=unserialize($source)) return;
360 spip_unlink($src); # on supprime la reference a sa source pour marquer cette image comme non intermediaire
362 ($fichier = $valeurs['fichier']) # l'origine est connue (on ne verifie pas son existence, qu'importe ...)
363 AND (substr($fichier,0,strlen(_DIR_VAR
))==_DIR_VAR
) # et est dans local
364 AND (lire_fichier($src = "$fichier.src",$source)) # le fichier a une source connue (c'est donc une image calculee intermediaire)
365 AND ($valeurs=unserialize($source)) # et valide
367 # on efface le fichier
368 spip_unlink($fichier);
369 # mais laisse le .src qui permet de savoir comment reconstruire l'image si besoin
374 // http://doc.spip.org/@image_graver
375 function image_graver($img){
376 // appeler le filtre post_image_filtrer qui permet de faire
377 // des traitements auto a la fin d'une serie de filtres
378 $img = pipeline('post_image_filtrer',$img);
380 $fichier = extraire_attribut($img, 'src');
381 if (($p=strpos($fichier,'?'))!==FALSE)
382 $fichier=substr($fichier,0,$p);
383 if (strlen($fichier) < 1)
385 # si jamais le fichier final n'a pas ete calcule car suppose temporaire
386 if (!@file_exists
($fichier))
387 reconstruire_image_intermediaire($fichier);
388 ramasse_miettes($fichier);
389 return $img; // on ne change rien
392 // Transforme une image a palette indexee (256 couleurs max) en "vraies" couleurs RGB
393 // Existe seulement pour compatibilite avec PHP < 5.5
394 // http://doc.spip.org/@imagepalettetotruecolor
395 if (!function_exists("imagepalettetotruecolor")) {
396 function imagepalettetotruecolor(&$img) {
397 if ($img AND !imageistruecolor($img) AND function_exists('imagecreatetruecolor')) {
400 $img1 = imagecreatetruecolor($w,$h);
401 //Conserver la transparence si possible
402 if(function_exists('ImageCopyResampled')) {
403 if (function_exists("imageAntiAlias")) imageAntiAlias($img1,true);
404 @imagealphablending
($img1, false);
405 @imagesavealpha
($img1,true);
406 @ImageCopyResampled
($img1, $img, 0, 0, 0, 0, $w, $h, $w, $h);
408 imagecopy($img1,$img,0,0,0,0,$w,$h);
416 // http://doc.spip.org/@image_tag_changer_taille
417 function _image_tag_changer_taille($tag,$width,$height,$style=false){
418 if ($style===false) $style = extraire_attribut($tag,'style');
419 // enlever le width et height du style
420 $style = preg_replace(",(^|;)\s*(width|height)\s*:\s*[^;]+,ims","",$style);
421 if ($style AND $style{0}==';') $style=substr($style,1);
422 // mettre des attributs de width et height sur les images,
423 // ca accelere le rendu du navigateur
424 // ca permet aux navigateurs de reserver la bonne taille
425 // quand on a desactive l'affichage des images.
426 $tag = inserer_attribut($tag,'width',$width);
427 $tag = inserer_attribut($tag,'height',$height);
428 $style = "height:".$height."px;width:".$width."px;".$style;
429 // attributs deprecies. Transformer en CSS
430 if ($espace = extraire_attribut($tag, 'hspace')){
431 $style = "margin:${espace}px;".$style;
432 $tag = inserer_attribut($tag,'hspace','');
434 $tag = inserer_attribut($tag,'style',$style);
438 // function d'ecriture du de la balise img en sortie des filtre image
439 // reprend le tag initial et surcharge les tags modifies
440 function _image_ecrire_tag($valeurs,$surcharge=array()){
441 $tag = str_replace(">","/>",str_replace("/>",">",$valeurs['tag'])); // fermer les tags img pas bien fermes;
444 $style = $valeurs['style'];
445 if (isset($surcharge['style'])){
446 $style = $surcharge['style'];
447 unset($surcharge['style']);
450 // traiter specifiquement la largeur et la hauteur
451 $width = $valeurs['largeur'];
452 if (isset($surcharge['width'])){
453 $width = $surcharge['width'];
454 unset($surcharge['width']);
456 $height = $valeurs['hauteur'];
457 if (isset($surcharge['height'])){
458 $height = $surcharge['height'];
459 unset($surcharge['height']);
462 $tag = _image_tag_changer_taille($tag,$width,$height,$style);
463 // traiter specifiquement le src qui peut etre repris dans un onmouseout
464 // on remplace toute les ref a src dans le tag
465 $src = extraire_attribut($tag,'src');
466 if (isset($surcharge['src'])){
467 $tag = str_replace($src,$surcharge['src'],$tag);
468 // si il y a des & dans src, alors ils peuvent provenir d'un &
469 // pas garanti comme methode, mais mieux que rien
470 if (strpos($src,'&') !== false)
471 $tag = str_replace(str_replace("&","&",$src),$surcharge['src'],$tag);
472 $src = $surcharge['src'];
473 unset($surcharge['src']);
476 $class = $valeurs['class'];
477 if (isset($surcharge['class'])){
478 $class = $surcharge['class'];
479 unset($surcharge['class']);
482 $tag = inserer_attribut($tag,'class',$class);
484 if (count($surcharge))
485 foreach($surcharge as $attribut=>$valeur)
486 $tag = inserer_attribut($tag,$attribut,$valeur);
491 function _image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process='AUTO', $force=false, $test_cache_only = false) {
492 // ordre de preference des formats graphiques pour creer les vignettes
493 // le premier format disponible, selon la methode demandee, est utilise
494 $image = $valeurs['fichier'];
495 $format = $valeurs['format_source'];
496 $destdir = dirname($valeurs['fichier_dest']);
497 $destfile = basename($valeurs['fichier_dest'],".".$valeurs["format_dest"]);
499 $format_sortie = $valeurs['format_dest'];
501 // liste des formats qu'on sait lire
502 $img = isset($GLOBALS['meta']['formats_graphiques'])
503 ?
(strpos($GLOBALS['meta']['formats_graphiques'], $format)!==false)
506 // si le doc n'est pas une image, refuser
507 if (!$force AND !$img) return;
508 $destination = "$destdir/$destfile";
512 if ($test_cache_only AND !$vignette) return;
514 // utiliser le cache ?
515 if (!$test_cache_only)
516 if ($force OR !$vignette OR (@filemtime
($vignette) < @filemtime
($image))) {
519 // calculer la taille
520 if (($srcWidth=$valeurs['largeur']) && ($srcHeight=$valeurs['hauteur'])){
521 if (!($destWidth=$valeurs['largeur_dest']) ||
!($destHeight=$valeurs['hauteur_dest']))
522 list ($destWidth,$destHeight) = _image_ratio($valeurs['largeur'], $valeurs['hauteur'], $maxWidth, $maxHeight);
524 elseif ($process == 'convert' OR $process == 'imagick') {
525 $destWidth = $maxWidth;
526 $destHeight = $maxHeight;
528 spip_log("echec $process sur $image");
532 // Si l'image est de la taille demandee (ou plus petite), simplement
535 AND $srcWidth <= $maxWidth AND $srcHeight <= $maxHeight) {
536 $vignette = $destination.'.'.$format;
537 @copy
($image, $vignette);
539 // imagemagick en ligne de commande
540 else if ($process == 'convert') {
541 define('_CONVERT_COMMAND', 'convert');
542 define ('_RESIZE_COMMAND', _CONVERT_COMMAND
.' -quality 85 -resize %xx%y! %src %dest');
543 $vignette = $destination.".".$format_sortie;
544 $commande = str_replace(
545 array('%x', '%y', '%src', '%dest'),
549 escapeshellcmd($image),
550 escapeshellcmd($vignette)
555 if (!@file_exists
($vignette)) {
556 spip_log("echec convert sur $vignette");
557 return; // echec commande
561 // imagick (php4-imagemagick)
562 if ($process == 'imagick') {
563 $vignette = "$destination.".$format_sortie;
564 $handle = imagick_readimage($image);
565 imagick_resize($handle, $destWidth, $destHeight, IMAGICK_FILTER_LANCZOS
, 0.75);
566 imagick_write($handle, $vignette);
567 if (!@file_exists
($vignette)) {
568 spip_log("echec imagick sur $vignette");
574 if ($process == "netpbm") {
575 define('_PNMSCALE_COMMAND', 'pnmscale'); // chemin a changer dans mes_options
576 if (_PNMSCALE_COMMAND
== '') return;
577 $vignette = $destination.".".$format_sortie;
578 $pnmtojpeg_command = str_replace("pnmscale", "pnmtojpeg", _PNMSCALE_COMMAND
);
579 if ($format == "jpg") {
581 $jpegtopnm_command = str_replace("pnmscale", "jpegtopnm", _PNMSCALE_COMMAND
);
582 exec("$jpegtopnm_command $image | "._PNMSCALE_COMMAND
." -width $destWidth | $pnmtojpeg_command > $vignette");
583 if (!($s = @filesize
($vignette)))
584 spip_unlink($vignette);
585 if (!@file_exists
($vignette)) {
586 spip_log("echec netpbm-jpg sur $vignette");
589 } else if ($format == "gif") {
590 $giftopnm_command = str_replace("pnmscale", "giftopnm", _PNMSCALE_COMMAND
);
591 exec("$giftopnm_command $image | "._PNMSCALE_COMMAND
." -width $destWidth | $pnmtojpeg_command > $vignette");
592 if (!($s = @filesize
($vignette)))
593 spip_unlink($vignette);
594 if (!@file_exists
($vignette)) {
595 spip_log("echec netpbm-gif sur $vignette");
598 } else if ($format == "png") {
599 $pngtopnm_command = str_replace("pnmscale", "pngtopnm", _PNMSCALE_COMMAND
);
600 exec("$pngtopnm_command $image | "._PNMSCALE_COMMAND
." -width $destWidth | $pnmtojpeg_command > $vignette");
601 if (!($s = @filesize
($vignette)))
602 spip_unlink($vignette);
603 if (!@file_exists
($vignette)) {
604 spip_log("echec netpbm-png sur $vignette");
610 else if ($process == 'gd1' OR $process == 'gd2') {
611 if (_IMG_GD_MAX_PIXELS
&& $srcWidth*$srcHeight>_IMG_GD_MAX_PIXELS
){
612 spip_log("vignette gd1/gd2 impossible : ".$srcWidth*$srcHeight."pixels");
615 $destFormat = $format_sortie;
617 spip_log("pas de format pour $image");
621 $fonction_imagecreatefrom = $valeurs['fonction_imagecreatefrom'];
622 if (!function_exists($fonction_imagecreatefrom))
624 $srcImage = @$fonction_imagecreatefrom($image);
626 spip_log("echec gd1/gd2");
630 // Initialisation de l'image destination
631 if ($process == 'gd2' AND $destFormat != "gif")
632 $destImage = ImageCreateTrueColor($destWidth, $destHeight);
634 $destImage = ImageCreate($destWidth, $destHeight);
636 // Recopie de l'image d'origine avec adaptation de la taille
638 if (($process == 'gd2') AND function_exists('ImageCopyResampled')) {
639 if ($format == "gif") {
640 // Si un GIF est transparent,
641 // fabriquer un PNG transparent
642 $transp = imagecolortransparent($srcImage);
643 if ($transp > 0) $destFormat = "png";
645 if ($destFormat == "png") {
646 // Conserver la transparence
647 if (function_exists("imageAntiAlias")) imageAntiAlias($destImage,true);
648 @imagealphablending
($destImage, false);
649 @imagesavealpha
($destImage,true);
651 $ok = @ImageCopyResampled
($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
654 $ok = ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
656 // Sauvegarde de l'image destination
657 $valeurs['fichier_dest'] = $vignette = "$destination.$destFormat";
658 $valeurs['format_dest'] = $format = $destFormat;
659 _image_gd_output($destImage,$valeurs);
662 ImageDestroy($srcImage);
663 ImageDestroy($destImage);
666 $size = @getimagesize
($vignette);
667 // Gaffe: en safe mode, pas d'acces a la vignette,
668 // donc risque de balancer "width='0'", ce qui masque l'image sous MSIE
669 if ($size[0] < 1) $size[0] = $destWidth;
670 if ($size[1] < 1) $size[1] = $destHeight;
672 $retour['width'] = $largeur = $size[0];
673 $retour['height'] = $hauteur = $size[1];
675 $retour['fichier'] = $vignette;
676 $retour['format'] = $format;
677 $retour['date'] = (file_exists($vignette)) ?
filemtime($vignette) : 0;
684 // http://doc.spip.org/@image_ratio
685 function _image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
686 $ratioWidth = $srcWidth/$maxWidth;
687 $ratioHeight = $srcHeight/$maxHeight;
689 if ($ratioWidth <=1 AND $ratioHeight <=1) {
690 $destWidth = $srcWidth;
691 $destHeight = $srcHeight;
692 } else if ($ratioWidth < $ratioHeight) {
693 $destWidth = $srcWidth/$ratioHeight;
694 $destHeight = $maxHeight;
697 $destWidth = $maxWidth;
698 $destHeight = $srcHeight/$ratioWidth;
700 return array (ceil($destWidth), ceil($destHeight),
701 max($ratioWidth,$ratioHeight));
704 // Calculer le ratio ajuste sur la plus petite dimension
705 // http://doc.spip.org/@ratio_passe_partout
706 function ratio_passe_partout ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
707 $ratioWidth = $srcWidth/$maxWidth;
708 $ratioHeight = $srcHeight/$maxHeight;
710 if ($ratioWidth <=1 AND $ratioHeight <=1) {
711 $destWidth = $srcWidth;
712 $destHeight = $srcHeight;
713 } else if ($ratioWidth > $ratioHeight) {
714 $destWidth = $srcWidth/$ratioHeight;
715 $destHeight = $maxHeight;
718 $destWidth = $maxWidth;
719 $destHeight = $srcHeight/$ratioWidth;
721 return array (ceil($destWidth), ceil($destHeight),
722 min($ratioWidth,$ratioHeight));
725 // http://doc.spip.org/@process_image_reduire
726 function process_image_reduire($fonction,$img,$taille,$taille_y,$force,$cherche_image,$process){
728 if (($process == 'AUTO') AND isset($GLOBALS['meta']['image_process']))
729 $process = $GLOBALS['meta']['image_process'];
730 # determiner le format de sortie
731 $format_sortie = false; // le choix par defaut sera bon
732 if ($process == "netpbm") $format_sortie = "jpg";
733 else if ($process == 'gd1' OR $process == 'gd2') {
734 $image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);
736 // on verifie que l'extension choisie est bonne (en principe oui)
737 $gd_formats = explode(',',$GLOBALS['meta']["gd_formats"]);
738 if (!in_array($image['format_dest'],$gd_formats)
739 OR ($image['format_dest']=='gif' AND !function_exists('ImageGif'))
741 if ($image['format_source'] == 'jpg')
742 $formats_sortie = array('jpg','png','gif');
743 else // les gif sont passes en png preferentiellement pour etre homogene aux autres filtres images
744 $formats_sortie = array('png','jpg','gif');
745 // Choisir le format destination
746 // - on sauve de preference en JPEG (meilleure compression)
747 // - pour le GIF : les GD recentes peuvent le lire mais pas l'ecrire
748 # bug : gd_formats contient la liste des fichiers qu'on sait *lire*,
751 foreach ($formats_sortie as $fmt) {
752 if (in_array($fmt, $gd_formats)) {
753 if ($fmt <> "gif" OR function_exists('ImageGif'))
754 $format_sortie = $fmt;
763 $image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);
765 if (!$image OR !$image['largeur'] OR !$image['hauteur']){
766 spip_log("image_reduire_src:pas de version locale de $img");
767 // on peut resizer en mode html si on dispose des elements
768 if ($srcw = extraire_attribut($img, 'width')
769 AND $srch = extraire_attribut($img, 'height')) {
770 list($w,$h) = _image_ratio($srcw, $srch, $taille, $taille_y);
771 return _image_tag_changer_taille($img,$w,$h);
773 // la on n'a pas d'infos sur l'image source... on refile le truc a css
774 // sous la forme style='max-width: NNpx;'
775 return inserer_attribut($img, 'style',
776 "max-width: ${taille}px; max-height: ${taille_y}px");
779 // si l'image est plus petite que la cible retourner une copie cachee de l'image
780 if (($image['largeur']<=$taille)&&($image['hauteur']<=$taille_y)){
781 if ($image['creer']){
782 @copy
($image['fichier'], $image['fichier_dest']);
784 return _image_ecrire_tag($image,array('src'=>$image['fichier_dest']));
787 if ($image['creer']==false && !$force)
788 return _image_ecrire_tag($image,array('src'=>$image['fichier_dest'],'width'=>$image['largeur_dest'],'height'=>$image['hauteur_dest']));
791 $cherche = cherche_image_nommee(substr($image['fichier'],0,-4), array($image["format_source"]));
792 if (!$cherche) return $img;
793 //list($chemin,$nom,$format) = $cherche;
795 if (in_array($image["format_source"],array('jpg','gif','png'))){
796 $destWidth = $image['largeur_dest'];
797 $destHeight = $image['hauteur_dest'];
798 $logo = $image['fichier'];
799 $date = $image["date_src"];
800 $preview = _image_creer_vignette($image, $taille, $taille_y,$process,$force);
802 if ($preview && $preview['fichier']) {
803 $logo = $preview['fichier'];
804 $destWidth = $preview['width'];
805 $destHeight = $preview['height'];
806 $date = $preview['date'];
808 // dans l'espace prive mettre un timestamp sur l'adresse
809 // de l'image, de facon a tromper le cache du navigateur
810 // quand on fait supprimer/reuploader un logo
811 // (pas de filemtime si SAFE MODE)
812 $date = test_espace_prive() ?
('?date='.$date) : '';
813 return _image_ecrire_tag($image,array('src'=>"$logo$date",'width'=>$destWidth,'height'=>$destHeight));
816 # SVG par exemple ? BMP, tiff ... les redacteurs osent tout!
821 // Produire des fichiers au format .ico
822 // avec du code recupere de :
824 //////////////////////////////////////////////////////////////
825 /// phpThumb() by James Heinrich <info@silisoftware.com> //
826 // available at http://phpthumb.sourceforge.net ///
827 //////////////////////////////////////////////////////////////
828 class phpthumb_functions
{
829 // http://doc.spip.org/@GetPixelColor
830 function GetPixelColor(&$img, $x, $y) {
831 if (!is_resource($img)) {
834 return @ImageColorsForIndex
($img, @ImageColorAt
($img, $x, $y));
836 // http://doc.spip.org/@LittleEndian2String
837 function LittleEndian2String($number, $minbytes=1) {
839 while ($number > 0) {
840 $intstring = $intstring.chr($number & 255);
843 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT
);
845 // http://doc.spip.org/@GD2ICOstring
846 function GD2ICOstring(&$gd_image_array) {
847 foreach ($gd_image_array as $key => $gd_image) {
849 $ImageWidths[$key] = ImageSX($gd_image);
850 $ImageHeights[$key] = ImageSY($gd_image);
851 $bpp[$key] = ImageIsTrueColor($gd_image) ?
32 : 24;
852 $totalcolors[$key] = ImageColorsTotal($gd_image);
855 for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) {
856 for ($x = 0; $x < $ImageWidths[$key]; $x++
) {
857 $argb = phpthumb_functions
::GetPixelColor($gd_image, $x, $y);
858 $a = round(255 * ((127 - $argb['alpha']) / 127));
863 if ($bpp[$key] == 32) {
864 $icXOR[$key] .= chr($b).chr($g).chr($r).chr($a);
865 } elseif ($bpp[$key] == 24) {
866 $icXOR[$key] .= chr($b).chr($g).chr($r);
870 @$icANDmask[$key][$y] .= '1';
872 @$icANDmask[$key][$y] .= '0';
875 // mask bits are 32-bit aligned per scanline
876 while (strlen($icANDmask[$key][$y]) %
32) {
877 $icANDmask[$key][$y] .= '0';
881 foreach ($icANDmask[$key] as $y => $scanlinemaskbits) {
882 for ($i = 0; $i < strlen($scanlinemaskbits); $i +
= 8) {
883 $icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT
)));
889 foreach ($gd_image_array as $key => $gd_image) {
890 $biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);
892 // BITMAPINFOHEADER - 40 bytes
893 $BitmapInfoHeader[$key] = '';
894 $BitmapInfoHeader[$key] .= "\x28\x00\x00\x00"; // DWORD biSize;
895 $BitmapInfoHeader[$key] .= phpthumb_functions
::LittleEndian2String($ImageWidths[$key], 4); // LONG biWidth;
896 // The biHeight member specifies the combined
897 // height of the XOR and AND masks.
898 $BitmapInfoHeader[$key] .= phpthumb_functions
::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG biHeight;
899 $BitmapInfoHeader[$key] .= "\x01\x00"; // WORD biPlanes;
900 $BitmapInfoHeader[$key] .= chr($bpp[$key])."\x00"; // wBitCount;
901 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biCompression;
902 $BitmapInfoHeader[$key] .= phpthumb_functions
::LittleEndian2String($biSizeImage, 4); // DWORD biSizeImage;
903 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // LONG biXPelsPerMeter;
904 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // LONG biYPelsPerMeter;
905 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biClrUsed;
906 $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biClrImportant;
910 $icondata = "\x00\x00"; // idReserved; // Reserved (must be 0)
911 $icondata .= "\x01\x00"; // idType; // Resource Type (1 for icons)
912 $icondata .= phpthumb_functions
::LittleEndian2String(count($gd_image_array), 2); // idCount; // How many images?
914 $dwImageOffset = 6 +
(count($gd_image_array) * 16);
915 foreach ($gd_image_array as $key => $gd_image) {
916 // ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
918 $icondata .= chr($ImageWidths[$key]); // bWidth; // Width, in pixels, of the image
919 $icondata .= chr($ImageHeights[$key]); // bHeight; // Height, in pixels, of the image
920 $icondata .= chr($totalcolors[$key]); // bColorCount; // Number of colors in image (0 if >=8bpp)
921 $icondata .= "\x00"; // bReserved; // Reserved ( must be 0)
923 $icondata .= "\x01\x00"; // wPlanes; // Color Planes
924 $icondata .= chr($bpp[$key])."\x00"; // wBitCount; // Bits per pixel
926 $dwBytesInRes = 40 +
strlen($icXOR[$key]) +
strlen($icAND[$key]);
927 $icondata .= phpthumb_functions
::LittleEndian2String($dwBytesInRes, 4); // dwBytesInRes; // How many bytes in this resource?
929 $icondata .= phpthumb_functions
::LittleEndian2String($dwImageOffset, 4); // dwImageOffset; // Where in the file is this image?
930 $dwImageOffset +
= strlen($BitmapInfoHeader[$key]);
931 $dwImageOffset +
= strlen($icXOR[$key]);
932 $dwImageOffset +
= strlen($icAND[$key]);
935 foreach ($gd_image_array as $key => $gd_image) {
936 $icondata .= $BitmapInfoHeader[$key];
937 $icondata .= $icXOR[$key];
938 $icondata .= $icAND[$key];