[SPIP] ~2.1.12 -->2.1.25
[velocampus/web/www.git] / www / ecrire / inc / filtres_images_lib_mini.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
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 \***************************************************************************/
12
13
14 if (!defined('_ECRIRE_INC_VERSION')) return;
15 include_spip('inc/filtres'); // par precaution
16
17
18 /**
19 * Transforme une couleur vectorielle R,G,B en hexa
20 *
21 * @param int $red
22 * @param int $green
23 * @param int $blue
24 * @return string
25 */
26 function _couleur_dec_to_hex($red, $green, $blue) {
27 $red = dechex($red);
28 $green = dechex($green);
29 $blue = dechex($blue);
30
31 if (strlen($red) == 1) $red = "0".$red;
32 if (strlen($green) == 1) $green = "0".$green;
33 if (strlen($blue) == 1) $blue = "0".$blue;
34
35 return "$red$green$blue";
36 }
37
38 /**
39 * Transforme une couleur hexa en vectorielle R,G,B
40 *
41 * @param string $couleur
42 * @return array
43 */
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));
51
52 return $retour;
53 }
54
55
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'))
60 $statut = false;
61 if ($stat==='get') return $statut;
62 $statut = $stat?true:false;
63 }
64
65 // http://doc.spip.org/@cherche_image_nommee
66 function cherche_image_nommee($nom, $formats = array ('gif', 'jpg', 'png')) {
67
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);
74 }
75 $pos = strrpos($nom, "/");
76 if ($pos > 0) {
77 $chemin = substr($nom, 0, $pos+1);
78 $nom = substr($nom, $pos+1);
79 } else {
80 $chemin = "";
81 }
82
83 reset($formats);
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);
91 }
92 }
93 }
94
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;
101
102 $source = trim(extraire_attribut($img, 'src'));
103 if (strlen($source) < 1){
104 $source = $img;
105 $img = "<img src='$source' />";
106 }
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]));
112 }
113 $source = $local;
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', '');
118 }
119
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 "";
125 } else {
126 // enlever le timestamp eventuel
127 $source=preg_replace(',[?][0-9]+$,','',$source);
128 $fichier = $source;
129 }
130
131 $terminaison_dest = "";
132 if (preg_match(",\.(gif|jpe?g|png)($|[?]),i", $fichier, $regs)) {
133 $terminaison = strtolower($regs[1]);
134 $terminaison_dest = $terminaison;
135
136 if ($terminaison == "gif") $terminaison_dest = "png";
137 }
138 if ($forcer_format!==false) $terminaison_dest = $forcer_format;
139
140 if (!$terminaison_dest) return false;
141
142 $term_fonction = $terminaison;
143 if ($term_fonction == "jpg") $term_fonction = "jpeg";
144
145 $nom_fichier = substr($fichier, 0, strlen($fichier) - (strlen($terminaison) + 1));
146 $fichier_dest = $nom_fichier;
147
148 if (@file_exists($f = $fichier)){
149 list ($ret["hauteur"],$ret["largeur"]) = taille_image($img);
150 $date_src = filemtime($f);
151 }
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"];
160 }
161 // pas de fichier source par la
162 else
163 return false;
164
165 // pas de taille mesurable
166 if (!($ret["hauteur"] OR $ret["largeur"]))
167 return false;
168
169
170 // cas general :
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);
188 }
189 else
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;
197 }*/
198 }
199 else {
200 $fichier_dest = md5("$fichier-$effet");
201 $cache = sous_repertoire(_DIR_VAR, $cache);
202 }
203
204 $fichier_dest = $cache . $fichier_dest . "." .$terminaison_dest;
205 $GLOBALS["images_calculees"][] = $fichier_dest;
206
207 $creer = true;
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;
211 }
212 else {
213 if (@file_exists($f = $fichier_dest)){
214 if (filemtime($f)>=$date_src)
215 $creer = false;
216 }
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)
221 $creer = false;
222 }
223 if ($creer) {
224 if (!@file_exists($fichier)) {
225 if (!@file_exists("$fichier.src")) {
226 spip_log("Image absente : $fichier");
227 return false;
228 }
229 # on reconstruit l'image source absente a partir de la chaine des .src
230 reconstruire_image_intermediaire($fichier);
231 }
232 }
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');
247 $ret["tag"] = $img;
248
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);
255 }
256 return $ret;
257 }
258
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);
264
265 $taille_test = getimagesize($tmp);
266 if ($taille_test[0] < 1) return false;
267
268 spip_unlink($fichier); // le fichier peut deja exister
269 @rename($tmp, $fichier);
270 return $ret;
271 }
272
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);
278
279 $taille_test = getimagesize($tmp);
280 if ($taille_test[0] < 1) return false;
281
282
283 spip_unlink($fichier); // le fichier peut deja exister
284 @rename($tmp, $fichier);
285 return $ret;
286 }
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);
292
293 $taille_test = getimagesize($tmp);
294 if ($taille_test[0] < 1) return false;
295
296 spip_unlink($fichier); // le fichier peut deja exister
297 @rename($tmp, $fichier);
298 return $ret;
299 }
300 // http://doc.spip.org/@image_imageico
301 function _image_imageico($img, $fichier) {
302 $gd_image_array = array($img);
303
304 return ecrire_fichier($fichier, phpthumb_functions::GD2ICOstring($gd_image_array));
305 }
306
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'];
311 $ret = false;
312 #un flag pour reperer les images gravees
313 $lock =
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'));
316 if (
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
320 && !$lock
321 )
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);
327 }
328
329 return $ret;
330 }
331
332 // http://doc.spip.org/@reconstruire_image_intermediaire
333 function reconstruire_image_intermediaire($fichier_manquant){
334 $reconstruire = array();
335 $fichier = $fichier_manquant;
336 while (
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 ...)
341 ) {
342 spip_unlink($src); // si jamais on a un timeout pendant la reconstruction, elle se fera naturellement au hit suivant
343 $reconstruire[] = $valeurs['reconstruction'];
344 }
345 while (count($reconstruire)){
346 $r = array_pop($reconstruire);
347 $fonction = $r[0];
348 $args = $r[1];
349 call_user_func_array($fonction, $args);
350 }
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);
354 }
355
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
361 while (
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
366 ) {
367 # on efface le fichier
368 spip_unlink($fichier);
369 # mais laisse le .src qui permet de savoir comment reconstruire l'image si besoin
370 #spip_unlink($src);
371 }
372 }
373
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);
379
380 $fichier = extraire_attribut($img, 'src');
381 if (($p=strpos($fichier,'?'))!==FALSE)
382 $fichier=substr($fichier,0,$p);
383 if (strlen($fichier) < 1)
384 $fichier = $img;
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
390 }
391
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')) {
398 $w = imagesx($img);
399 $h = imagesy($img);
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);
407 } else {
408 imagecopy($img1,$img,0,0,0,0,$w,$h);
409 }
410
411 $img = $img1;
412 }
413 }
414 }
415
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','');
433 }
434 $tag = inserer_attribut($tag,'style',$style);
435 return $tag;
436 }
437
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;
442
443 // le style
444 $style = $valeurs['style'];
445 if (isset($surcharge['style'])){
446 $style = $surcharge['style'];
447 unset($surcharge['style']);
448 }
449
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']);
455 }
456 $height = $valeurs['hauteur'];
457 if (isset($surcharge['height'])){
458 $height = $surcharge['height'];
459 unset($surcharge['height']);
460 }
461
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 &amp
469 // pas garanti comme methode, mais mieux que rien
470 if (strpos($src,'&') !== false)
471 $tag = str_replace(str_replace("&","&amp;",$src),$surcharge['src'],$tag);
472 $src = $surcharge['src'];
473 unset($surcharge['src']);
474 }
475
476 $class = $valeurs['class'];
477 if (isset($surcharge['class'])){
478 $class = $surcharge['class'];
479 unset($surcharge['class']);
480 }
481 if(strlen($class))
482 $tag = inserer_attribut($tag,'class',$class);
483
484 if (count($surcharge))
485 foreach($surcharge as $attribut=>$valeur)
486 $tag = inserer_attribut($tag,$attribut,$valeur);
487
488 return $tag;
489 }
490
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"]);
498
499 $format_sortie = $valeurs['format_dest'];
500
501 // liste des formats qu'on sait lire
502 $img = isset($GLOBALS['meta']['formats_graphiques'])
503 ? (strpos($GLOBALS['meta']['formats_graphiques'], $format)!==false)
504 : false;
505
506 // si le doc n'est pas une image, refuser
507 if (!$force AND !$img) return;
508 $destination = "$destdir/$destfile";
509
510 // chercher un cache
511 $vignette = '';
512 if ($test_cache_only AND !$vignette) return;
513
514 // utiliser le cache ?
515 if (!$test_cache_only)
516 if ($force OR !$vignette OR (@filemtime($vignette) < @filemtime($image))) {
517
518 $creation = true;
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);
523 }
524 elseif ($process == 'convert' OR $process == 'imagick') {
525 $destWidth = $maxWidth;
526 $destHeight = $maxHeight;
527 } else {
528 spip_log("echec $process sur $image");
529 return;
530 }
531
532 // Si l'image est de la taille demandee (ou plus petite), simplement
533 // la retourner
534 if ($srcWidth
535 AND $srcWidth <= $maxWidth AND $srcHeight <= $maxHeight) {
536 $vignette = $destination.'.'.$format;
537 @copy($image, $vignette);
538 }
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'),
546 array(
547 $destWidth,
548 $destHeight,
549 escapeshellcmd($image),
550 escapeshellcmd($vignette)
551 ),
552 _RESIZE_COMMAND);
553 spip_log($commande);
554 exec($commande);
555 if (!@file_exists($vignette)) {
556 spip_log("echec convert sur $vignette");
557 return; // echec commande
558 }
559 }
560 else
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");
569 return;
570 }
571 }
572 else
573 // netpbm
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") {
580
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");
587 return;
588 }
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");
596 return;
597 }
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");
605 return;
606 }
607 }
608 }
609 // gd ou gd2
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");
613 return;
614 }
615 $destFormat = $format_sortie;
616 if (!$destFormat) {
617 spip_log("pas de format pour $image");
618 return;
619 }
620
621 $fonction_imagecreatefrom = $valeurs['fonction_imagecreatefrom'];
622 if (!function_exists($fonction_imagecreatefrom))
623 return '';
624 $srcImage = @$fonction_imagecreatefrom($image);
625 if (!$srcImage) {
626 spip_log("echec gd1/gd2");
627 return;
628 }
629
630 // Initialisation de l'image destination
631 if ($process == 'gd2' AND $destFormat != "gif")
632 $destImage = ImageCreateTrueColor($destWidth, $destHeight);
633 if (!$destImage)
634 $destImage = ImageCreate($destWidth, $destHeight);
635
636 // Recopie de l'image d'origine avec adaptation de la taille
637 $ok = false;
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";
644 }
645 if ($destFormat == "png") {
646 // Conserver la transparence
647 if (function_exists("imageAntiAlias")) imageAntiAlias($destImage,true);
648 @imagealphablending($destImage, false);
649 @imagesavealpha($destImage,true);
650 }
651 $ok = @ImageCopyResampled($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
652 }
653 if (!$ok)
654 $ok = ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
655
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);
660
661 if ($srcImage)
662 ImageDestroy($srcImage);
663 ImageDestroy($destImage);
664 }
665 }
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;
671
672 $retour['width'] = $largeur = $size[0];
673 $retour['height'] = $hauteur = $size[1];
674
675 $retour['fichier'] = $vignette;
676 $retour['format'] = $format;
677 $retour['date'] = (file_exists($vignette)) ? filemtime($vignette) : 0;
678
679 // renvoyer l'image
680 return $retour;
681 }
682
683 // Calculer le ratio
684 // http://doc.spip.org/@image_ratio
685 function _image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {
686 $ratioWidth = $srcWidth/$maxWidth;
687 $ratioHeight = $srcHeight/$maxHeight;
688
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;
695 }
696 else {
697 $destWidth = $maxWidth;
698 $destHeight = $srcHeight/$ratioWidth;
699 }
700 return array (ceil($destWidth), ceil($destHeight),
701 max($ratioWidth,$ratioHeight));
702 }
703
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;
709
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;
716 }
717 else {
718 $destWidth = $maxWidth;
719 $destHeight = $srcHeight/$ratioWidth;
720 }
721 return array (ceil($destWidth), ceil($destHeight),
722 min($ratioWidth,$ratioHeight));
723 }
724
725 // http://doc.spip.org/@process_image_reduire
726 function process_image_reduire($fonction,$img,$taille,$taille_y,$force,$cherche_image,$process){
727 $image = false;
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);
735
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'))
740 ) {
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*,
749 # pas *ecrire*
750 $format_sortie = "";
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;
755 break;
756 }
757 }
758 $image = false;
759 }
760 }
761
762 if (!$image)
763 $image = _image_valeurs_trans($img, "reduire-{$taille}-{$taille_y}",$format_sortie,$fonction);
764
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);
772 }
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");
777 }
778
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']);
783 }
784 return _image_ecrire_tag($image,array('src'=>$image['fichier_dest']));
785 }
786
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']));
789
790 if ($cherche_image){
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;
794 }
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);
801
802 if ($preview && $preview['fichier']) {
803 $logo = $preview['fichier'];
804 $destWidth = $preview['width'];
805 $destHeight = $preview['height'];
806 $date = $preview['date'];
807 }
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));
814 }
815 else
816 # SVG par exemple ? BMP, tiff ... les redacteurs osent tout!
817 return $img;
818 }
819
820 //
821 // Produire des fichiers au format .ico
822 // avec du code recupere de :
823 //
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)) {
832 return false;
833 }
834 return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
835 }
836 // http://doc.spip.org/@LittleEndian2String
837 function LittleEndian2String($number, $minbytes=1) {
838 $intstring = '';
839 while ($number > 0) {
840 $intstring = $intstring.chr($number & 255);
841 $number >>= 8;
842 }
843 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
844 }
845 // http://doc.spip.org/@GD2ICOstring
846 function GD2ICOstring(&$gd_image_array) {
847 foreach ($gd_image_array as $key => $gd_image) {
848
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);
853
854 $icXOR[$key] = '';
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));
859 $r = $argb['red'];
860 $g = $argb['green'];
861 $b = $argb['blue'];
862
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);
867 }
868
869 if ($a < 128) {
870 @$icANDmask[$key][$y] .= '1';
871 } else {
872 @$icANDmask[$key][$y] .= '0';
873 }
874 }
875 // mask bits are 32-bit aligned per scanline
876 while (strlen($icANDmask[$key][$y]) % 32) {
877 $icANDmask[$key][$y] .= '0';
878 }
879 }
880 $icAND[$key] = '';
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)));
884 }
885 }
886
887 }
888
889 foreach ($gd_image_array as $key => $gd_image) {
890 $biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8);
891
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;
907 }
908
909
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?
913
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)
917
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)
922
923 $icondata .= "\x01\x00"; // wPlanes; // Color Planes
924 $icondata .= chr($bpp[$key])."\x00"; // wBitCount; // Bits per pixel
925
926 $dwBytesInRes = 40 + strlen($icXOR[$key]) + strlen($icAND[$key]);
927 $icondata .= phpthumb_functions::LittleEndian2String($dwBytesInRes, 4); // dwBytesInRes; // How many bytes in this resource?
928
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]);
933 }
934
935 foreach ($gd_image_array as $key => $gd_image) {
936 $icondata .= $BitmapInfoHeader[$key];
937 $icondata .= $icXOR[$key];
938 $icondata .= $icAND[$key];
939 }
940
941 return $icondata;
942 }
943
944 }
945
946 ?>