1<?php 2/** 3 * image processing functions 4 * @package core 5 * @subpackage functions\functions-image 6 * 7 */ 8// force UTF-8 Ø 9// functions-image.php - HEADERS NOT SENT YET! 10 11/** 12 * If in debug mode, prints the given error message and continues; otherwise redirects 13 * to the given error message image and exits; designed for a production gallery. 14 * 15 * Nothing is but logged only if debuglog_error/returnmode mode is enabled 16 * 17 * @param type $errormessage 18 * @param type $errorimg 19 */ 20 21/** 22 * 23 * @global string $newfilename 24 * @global string $album 25 * @global string $image 26 * @param string $status_text 27 * @param string $errormessage the error message to print if $_GET['debug'] is set. 28 * @param string $errorimg the filename of the error image to display for production. Defaults to 'err-imagegeneral.png'. Images should be located in /zen/images . 29 * @param string $image 30 * @param string $album 31 * @param string $newfilename 32 */ 33function imageError($status_text, $errormessage, $errorimg = 'err-imagegeneral.png', $image = '', $album='', $newfilename = '') { 34 //global $newfilename, $album, $image; // sometime these globals need to be properly named… 35 $debug = isset($_GET['debug']); 36 $debuglog_errors = isset($_GET['returnmode']); 37 if ($debug) { 38 $debugnote = '<strong>' . sprintf(gettext('Zenphoto Image Processing Error: %s'), $errormessage) . '</strong>'; 39 $debugnote .= '<br /><br />' . sprintf(gettext('Request URI: [ <code>%s</code> ]'), html_encode(getRequestURI())); 40 $debugnote .= '<br />PHP_SELF: [ <code>' . html_encode($_SERVER['PHP_SELF']) . '</code> ]'; 41 $debugnote .= (empty($newfilename) ? '' : '<br />' . sprintf(gettext('Cache: [<code>%s</code>]'), '/' . CACHEFOLDER . '/' . html_encode(sanitize($newfilename, 3))) . ' '); 42 $debugnote .= (empty($image) || empty($album) ? '' : ' <br />' . sprintf(gettext('Image: [<code>%s</code>]'), html_encode(sanitize($album . '/' . $image, 3))) . ' <br />'); 43 if($debuglog_errors) { 44 debugLog($debugnote); 45 } else { 46 echo $debugnote; 47 } 48 } else { 49 if (DEBUG_IMAGE_ERR) { 50 trigger_error($errormessage, E_USER_NOTICE); 51 } 52 if(!$debuglog_errors) { 53 header("HTTP/1.0 $status_text"); 54 header("Status: $status_text"); 55 redirectURL(FULLWEBPATH . '/' . ZENFOLDER . '/images/' . $errorimg); 56 } 57 } 58 exitZP(); 59} 60 61/** 62 * Prints debug information from the arguments to i.php. 63 * 64 * @param string $album alubm name 65 * @param string $image image name 66 * @param array $args size/crop arguments 67 * @param string $imgfile the filename of the image 68 */ 69function imageDebug($album, $image, $args, $imgfile) { 70 list($size, $width, $height, $cw, $ch, $cx, $cy, $quality, $thumb, $crop) = $args; 71 echo "Album: [ " . $album . " ], Image: [ " . $image . " ]<br /><br />"; 72 if (file_exists($imgfile)) { 73 echo "Image filesize: " . filesize($imgfile); 74 } else { 75 echo "Image file not found."; 76 } 77 echo '<br /><br />'; 78 echo "<strong>" . gettext("Debug") . " <code>i.php</code> | " . gettext("Arguments:") . "</strong><br />\n\n" 79 ?> 80 <ul> 81 <li><?php echo gettext("size ="); ?> <strong> <?php echo sanitize($size, 3) ?> </strong></li> 82 <li><?php echo gettext("width =") ?> <strong> <?php echo sanitize($width, 3) ?> </strong></li> 83 <li><?php echo gettext("height =") ?> <strong> <?php echo sanitize($height, 3) ?> </strong></li> 84 <li><?php echo gettext("cw =") ?> <strong> <?php echo sanitize($cw, 3) ?> </strong></li> 85 <li><?php echo gettext("ch =") ?> <strong> <?php echo sanitize($ch, 3) ?> </strong></li> 86 <li><?php echo gettext("cx =") ?> <strong> <?php echo sanitize($cx, 3) ?> </strong></li> 87 <li><?php echo gettext("cy =") ?> <strong> <?php echo sanitize($cy, 3) ?> </strong></li> 88 <li><?php echo gettext("quality =") ?> <strong> <?php echo sanitize($quality, 3) ?> </strong></li> 89 <li><?php echo gettext("thumb =") ?> <strong> <?php echo sanitize($thumb, 3) ?> </strong></li> 90 <li><?php echo gettext("crop =") ?> <strong> <?php echo sanitize($crop, 3) ?> </strong></li> 91 </ul> 92 <?php 93} 94 95/** 96 * Calculates proprotional width and height 97 * Used internally by cacheImage 98 * 99 * Returns array containing the new width and height 100 * 101 * @param int $size 102 * @param int $width 103 * @param int $height 104 * @param int $w 105 * @param int $h 106 * @param int $thumb 107 * @param int $image_use_side 108 * @param int $dim 109 * @return array 110 */ 111function propSizes($size, $width, $height, $w, $h, $thumb, $image_use_side, $dim) { 112 $hprop = round(($h / $w) * $dim); 113 $wprop = round(($w / $h) * $dim); 114 if ($size) { 115 if (((($image_use_side == 'longest')) && $h > $w) || ($image_use_side == 'height') || ($image_use_side == 'shortest' && $h < $w)) { 116 $newh = $dim; // height is the size and width is proportional 117 $neww = $wprop; 118 } else { 119 $neww = $dim; // width is the size and height is proportional 120 $newh = $hprop; 121 } 122 } else { // length and/or width is set, size is NULL (Thumbs work the same as image in this case) 123 if ($height) { 124 $newh = $height; // height is supplied, use it 125 } else { 126 $newh = $hprop; // height not supplied, use the proprotional 127 } 128 if ($width) { 129 $neww = $width; // width is supplied, use it 130 } else { 131 $neww = $wprop; // width is not supplied, use the proportional 132 } 133 } 134 if (DEBUG_IMAGE) 135 debugLog("propSizes(\$size=$size, \$width=$width, \$height=$height, \$w=$w, \$h=$h, \$thumb=$thumb, \$image_use_side=$image_use_side, \$dim=$dim):: \$wprop=$wprop; \$hprop=$hprop; \$neww=$neww; \$newh=$newh"); 136 return array($neww, $newh); 137} 138 139/** 140 * iptc_make_tag() function by Thies C. Arntzen 141 * @param $rec 142 * @param $data 143 * @param $value 144 */ 145function iptc_make_tag($rec, $data, $value) { 146 $length = strlen($value); 147 $retval = chr(0x1C) . chr($rec) . chr($data); 148 if ($length < 0x8000) { 149 $retval .= chr($length >> 8) . chr($length & 0xFF); 150 } else { 151 $retval .= chr(0x80) . chr(0x04) . chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF); 152 } 153 return $retval . $value; 154} 155 156/** 157 * Creates the cache folder version of the image, including watermarking 158 * 159 * @param string $newfilename the name of the file when it is in the cache 160 * @param string $imgfile the image name 161 * @param array $args the cropping arguments 162 * @param bool $allow_watermark set to true if image may be watermarked 163 * @param string $theme the current theme 164 * @param string $album the album containing the image 165 */ 166function cacheImage($newfilename, $imgfile, $args, $allow_watermark = false, $theme, $album) { 167 global $_zp_gallery; 168 try { 169 @list($size, $width, $height, $cw, $ch, $cx, $cy, $quality, $thumb, $crop, $thumbstandin, $passedWM, $adminrequest, $effects) = $args; 170 // Set the config variables for convenience. 171 if($thumb) { 172 $image_use_side = getOption('thumb_use_side'); 173 } else { 174 $image_use_side = getOption('image_use_side'); 175 } 176 $upscale = getOption('image_allow_upscale'); 177 $allowscale = true; 178 $sharpenthumbs = getOption('thumb_sharpen'); 179 $sharpenimages = getOption('image_sharpen'); 180 $id = $im = NULL; 181 $watermark_use_image = getAlbumInherited($album, 'watermark', $id); 182 if (empty($watermark_use_image)) { 183 $watermark_use_image = IMAGE_WATERMARK; 184 } 185 if (!$effects) { 186 if ($thumb && getOption('thumb_gray')) { 187 $effects = 'gray'; 188 } else if (getOption('image_gray')) { 189 $effects = 'gray'; 190 } 191 } 192 $newfile = SERVERCACHE . $newfilename; 193 mkdir_recursive(dirname($newfile), FOLDER_MOD); 194 if (DEBUG_IMAGE) 195 debugLog("cacheImage(\$imgfile=" . basename($imgfile) . ", \$newfilename=$newfilename, \$allow_watermark=$allow_watermark, \$theme=$theme) \$size=$size, \$width=$width, \$height=$height, \$cw=$cw, \$ch=$ch, \$cx=" . (is_null($cx) ? 'NULL' : $cx) . ", \$cy=" . (is_null($cy) ? 'NULL' : $cy) . ", \$quality=$quality, \$thumb=$thumb, \$crop=$crop \$image_use_side=$image_use_side; \$upscale=$upscale);"); 196 // Check for the source image. 197 if (!file_exists($imgfile) || !is_readable($imgfile)) { 198 imageError('404 Not Found', sprintf(gettext('Image %s not found or is unreadable.'), filesystemToInternal($imgfile)), 'err-imagenotfound.png'); 199 } 200 $rotate = false; 201 if (zp_imageCanRotate()) { 202 $rotate = getImageRotation($imgfile); 203 } 204 $s = getSuffix($imgfile); 205 if (function_exists('exif_thumbnail') && getOption('use_embedded_thumb') && ($s == 'jpg' || $s == 'jpeg')) { 206 $im = exif_thumbnail($imgfile, $tw, $th, $tt); 207 if ($im) { 208 if ($size) { 209 $big_enough = $tw >= $size && $th >= $size; 210 } else { 211 $big_enough = $tw >= $width && $th >= $height; 212 } 213 if ($big_enough) { 214 $im = zp_imageFromString($im); 215 if (DEBUG_IMAGE && $im) 216 debugLog(sprintf(gettext('Using %1$ux%2$u %3$s thumbnail image.'), $tw, $th, image_type_to_mime_type($tt))); 217 } else { 218 $im = false; 219 } 220 } else { 221 $im = false; 222 } 223 } 224 if (!$im) { 225 $im = zp_imageGet($imgfile); 226 } 227 if (!$im) { 228 imageError('404 Not Found', sprintf(gettext('Image %s not renderable (imageGet).'), filesystemToInternal($imgfile)), 'err-failimage.png', $imgfile, $album, $newfilename); 229 } 230 if ($rotate) { 231 if (DEBUG_IMAGE) 232 debugLog("cacheImage:rotate->$rotate"); 233 $im = zp_rotateImage($im, $rotate); 234 if (!$im) { 235 imageError('404 Not Found', sprintf(gettext('Image %s not rotatable.'), filesystemToInternal($imgfile)), 'err-failimage.png', $imgfile, $album, $newfilename); 236 } 237 } 238 $w = zp_imageWidth($im); 239 $h = zp_imageHeight($im); 240 // Give the sizing dimension to $dim 241 $ratio_in = ''; 242 $ratio_out = ''; 243 $crop = ($crop || $cw != 0 || $ch != 0); 244 if (!empty($size)) { 245 $dim = $size; 246 if ($crop) { 247 $dim = $size; 248 if (!$ch) 249 $ch = $size; 250 if (!$cw) 251 $cw = $size; 252 $width = $cw; 253 $height = $ch; 254 $size = false; 255 } else { 256 $width = $height = false; 257 } 258 } else if (!empty($width) && !empty($height)) { 259 $ratio_in = $h / $w; 260 $ratio_out = $height / $width; 261 if ($ratio_in > $ratio_out) { // image is taller than desired, $height is the determining factor 262 $thumb = true; 263 $dim = $width; 264 if (!$ch) 265 $ch = $height; 266 } else { // image is wider than desired, $width is the determining factor 267 $dim = $height; 268 if (!$cw) 269 $cw = $width; 270 } 271 } else if (!empty($width)) { 272 $dim = $width; 273 $size = $height = false; 274 } else if (!empty($height)) { 275 $dim = $height; 276 $size = $width = false; 277 } else { 278 // There's a problem up there somewhere... 279 imageError('404 Not Found', sprintf(gettext('Unknown error processing %s! Please report to the developers at <a href="http://www.zenphoto.org/">www.zenphoto.org</a>'), filesystemToInternal($imgfile)), 'err-imagegeneral.png', $imgfile, $album, $newfilename); 280 } 281 282 $sizes = propSizes($size, $width, $height, $w, $h, $thumb, $image_use_side, $dim); 283 list($neww, $newh) = $sizes; 284 285 if (DEBUG_IMAGE) 286 debugLog("cacheImage:" . basename($imgfile) . ": \$size=$size, \$width=$width, \$height=$height, \$w=$w; \$h=$h; \$cw=$cw, " . 287 "\$ch=$ch, \$cx=$cx, \$cy=$cy, \$quality=$quality, \$thumb=$thumb, \$crop=$crop, \$newh=$newh, \$neww=$neww, \$dim=$dim, " . 288 "\$ratio_in=$ratio_in, \$ratio_out=$ratio_out \$upscale=$upscale \$rotate=$rotate \$effects=$effects"); 289 290 if (!$upscale && $newh >= $h && $neww >= $w) { // image is the same size or smaller than the request 291 $neww = $w; 292 $newh = $h; 293 $allowscale = false; 294 if ($crop) { 295 if ($width > $neww) { 296 $width = $neww; 297 } 298 if ($height > $newh) { 299 $height = $newh; 300 } 301 } 302 if (DEBUG_IMAGE) 303 debugLog("cacheImage:no upscale " . basename($imgfile) . ": \$newh=$newh, \$neww=$neww, \$crop=$crop, \$thumb=$thumb, \$rotate=$rotate, watermark=" . $watermark_use_image); 304 } 305 306 $watermark_image = false; 307 if ($passedWM) { 308 if ($passedWM != NO_WATERMARK) { 309 $watermark_image = getWatermarkPath($passedWM); 310 if (!file_exists($watermark_image)) { 311 $watermark_image = SERVERPATH . '/' . ZENFOLDER . '/images/imageDefault.png'; 312 } 313 } 314 } else { 315 if ($allow_watermark) { 316 $watermark_image = $watermark_use_image; 317 if ($watermark_image) { 318 if ($watermark_image != NO_WATERMARK) { 319 $watermark_image = getWatermarkPath($watermark_image); 320 if (!file_exists($watermark_image)) { 321 $watermark_image = SERVERPATH . '/' . ZENFOLDER . '/images/imageDefault.png'; 322 } 323 } 324 } 325 } 326 } 327 328 // Crop the image if requested. 329 if ($crop) { 330 if ($cw > $ch) { 331 $ir = $ch / $cw; 332 } else { 333 $ir = $cw / $ch; 334 } 335 if ($size) { 336 $neww = $size; 337 $newh = $ir * $size; 338 } else { 339 $neww = $width; 340 $newh = $height; 341 if ($neww > $newh) { 342 if ($newh === false) { 343 $newh = $ir * $neww; 344 } 345 } else { 346 if ($neww === false) { 347 $neww = $ir * $newh; 348 } 349 } 350 } 351 if (is_null($cx) && is_null($cy)) { // scale crop to max of image 352 // set crop scale factor 353 $cf = 1; 354 if ($cw) 355 $cf = min($cf, $cw / $neww); 356 if ($ch) 357 $cf = min($cf, $ch / $newh); 358 // set the image area of the crop (use the most image possible, rule of thirds positioning) 359 if (!$cw || $w / $cw * $ch > $h) { 360 $cw = round($h / $ch * $cw * $cf); 361 $ch = round($h * $cf); 362 $cx = round(($w - $cw) / 3); 363 } else { 364 $ch = round($w / $cw * $ch * $cf); 365 $cw = round($w * $cf); 366 $cy = round(($h - $ch) / 3); 367 } 368 } else { // custom crop 369 if (!$cw || $cw > $w) 370 $cw = $w; 371 if (!$ch || $ch > $h) 372 $ch = $h; 373 } 374 // force the crop to be within the image 375 if ($cw + $cx > $w) 376 $cx = $w - $cw; 377 if ($cx < 0) { 378 $cw = $cw + $cx; 379 $cx = 0; 380 } 381 if ($ch + $cy > $h) 382 $cy = $h - $ch; 383 if ($cy < 0) { 384 $ch = $ch + $cy; 385 $cy = 0; 386 } 387 if (DEBUG_IMAGE) 388 debugLog("cacheImage:crop " . basename($imgfile) . ":\$size=$size, \$width=$width, \$height=$height, \$cw=$cw, \$ch=$ch, \$cx=$cx, \$cy=$cy, \$quality=$quality, \$thumb=$thumb, \$crop=$crop, \$rotate=$rotate"); 389 switch (getSuffix($newfilename)) { 390 case 'gif': 391 $newim = zp_createImage($neww, $newh, false); 392 $newim = zp_imageResizeTransparent($newim, $neww, $newh); 393 break; 394 case 'png': 395 case 'webp': 396 default: 397 $newim = zp_createImage($neww, $newh); 398 if (in_array(getSuffix($newfilename), array('png', 'webp'))) { 399 $newim = zp_imageResizeAlpha($newim, $neww, $newh); 400 } 401 break; 402 } 403 if (!zp_resampleImage($newim, $im, 0, 0, $cx, $cy, $neww, $newh, $cw, $ch)) { 404 imageError('404 Not Found', sprintf(gettext('Image %s not renderable (resample).'), filesystemToInternal($imgfile)), 'err-failimage.png', $imgfile, $album, $newfilename); 405 } 406 } else { 407 if ($newh >= $h && $neww >= $w && !$rotate && !$effects && !$watermark_image && (!$upscale || $newh == $h && $neww == $w)) { 408 // we can just use the original! 409 if (SYMLINK && @symlink($imgfile, $newfile)) { 410 if (DEBUG_IMAGE) 411 debugLog("cacheImage:symlink original " . basename($imgfile) . ":\$size=$size, \$width=$width, \$height=$height, \$dim=$dim, \$neww=$neww; \$newh=$newh; \$quality=$quality, \$thumb=$thumb, \$crop=$crop, \$rotate=$rotate; \$allowscale=$allowscale;"); 412 clearstatcache(); 413 return true; 414 } else if (@copy($imgfile, $newfile)) { 415 if (DEBUG_IMAGE) 416 debugLog("cacheImage:copy original " . basename($imgfile) . ":\$size=$size, \$width=$width, \$height=$height, \$dim=$dim, \$neww=$neww; \$newh=$newh; \$quality=$quality, \$thumb=$thumb, \$crop=$crop, \$rotate=$rotate; \$allowscale=$allowscale;"); 417 clearstatcache(); 418 return true; 419 } 420 } 421 if ($allowscale) { 422 $sizes = propSizes($size, $width, $height, $w, $h, $thumb, $image_use_side, $dim); 423 list($neww, $newh) = $sizes; 424 } 425 if (DEBUG_IMAGE) 426 debugLog("cacheImage:no crop " . basename($imgfile) . ":\$size=$size, \$width=$width, \$height=$height, \$dim=$dim, \$neww=$neww; \$newh=$newh; \$quality=$quality, \$thumb=$thumb, \$crop=$crop, \$rotate=$rotate; \$allowscale=$allowscale;"); 427 428 switch (getSuffix($newfilename)) { 429 case 'gif': 430 $newim = zp_createImage($neww, $newh, false); 431 $newim = zp_imageResizeTransparent($newim, $neww, $newh); 432 break; 433 case 'png': 434 case 'webp': 435 default: 436 $newim = zp_createImage($neww, $newh); 437 if (in_array(getSuffix($newfilename), array('png', 'webp'))) { 438 $newim = zp_imageResizeAlpha($newim, $neww, $newh); 439 } 440 break; 441 } 442 if (!zp_resampleImage($newim, $im, 0, 0, 0, 0, $neww, $newh, $w, $h)) { 443 imageError('404 Not Found', sprintf(gettext('Image %s not renderable (resample).'), filesystemToInternal($imgfile)), 'err-failimage.png', $imgfile, $album, $newfilename); 444 } 445 if (($thumb && $sharpenthumbs) || (!$thumb && $sharpenimages)) { 446 if (!zp_imageUnsharpMask($newim, getOption('sharpen_amount'), getOption('sharpen_radius'), getOption('sharpen_threshold'))) { 447 imageError('404 Not Found', sprintf(gettext('Image %s not renderable (unsharp).'), filesystemToInternal($imgfile)), 'err-failimage.png', $imgfile, $album, $newfilename); 448 } 449 } 450 } 451 452 $imgEffects = explode(',', $effects); 453 if (in_array('gray', $imgEffects)) { 454 zp_imageGray($newim); 455 } 456 $newim = addWatermark($newim, $watermark_image, $imgfile); 457 458 // Create the cached file (with lots of compatibility)... 459 @chmod($newfile, 0777); 460 if (zp_imageOutput($newim, getSuffix($newfile), $newfile, $quality)) { // successful save of cached image 461 if (getOption('EmbedIPTC') && getSuffix($newfilename) == 'jpg' && GRAPHICS_LIBRARY != 'Imagick') { // the embed function works only with JPEG images 462 global $_zp_extra_filetypes; // because we are doing the require in a function! 463 if (!$_zp_extra_filetypes) 464 $_zp_extra_filetypes = array(); 465 require_once(dirname(__FILE__) . '/functions.php'); // it is ok to increase memory footprint now since the image processing is complete 466 $iptc = array( 467 '1#090' => chr(0x1b) . chr(0x25) . chr(0x47), // character set is UTF-8 468 '2#115' => $_zp_gallery->getTitle() // source 469 ); 470 $iptc_data = zp_imageIPTC($imgfile); 471 if ($iptc_data) { 472 $iptc_data = iptcparse($iptc_data); 473 if ($iptc_data) 474 $iptc = array_merge($iptc_data, $iptc); 475 } 476 $imgfile = str_replace(ALBUM_FOLDER_SERVERPATH, '', $imgfile); 477 $imagename = basename($imgfile); 478 $albumname = dirname($imgfile); 479 $image = newImage(newAlbum($albumname), $imagename); 480 $copyright = $image->getCopyright(); 481 if (empty($copyright)) { 482 $copyright = getOption('copyright_image_notice'); 483 } 484 if (!empty($copyright)) { 485 $iptc['2#116'] = $copyright; 486 } 487 $credit = $image->getCredit(); 488 if (!empty($credit)) { 489 $iptc['2#110'] = $credit; 490 } 491 $iptc_result = ''; 492 foreach ($iptc as $tag => $string) { 493 $tag_parts = explode('#', $tag); 494 if (is_array($string)) { 495 foreach ($string as $element) { 496 $iptc_result .= iptc_make_tag($tag_parts[0], $tag_parts[1], $element); 497 } 498 } else { 499 $iptc_result .= iptc_make_tag($tag_parts[0], $tag_parts[1], $string); 500 } 501 } 502 $content = iptcembed($iptc_result, $newfile); 503 $fw = fopen($newfile, 'w'); 504 fwrite($fw, $content); 505 fclose($fw); 506 clearstatcache(); 507 } 508 @chmod($newfile, FILE_MOD); 509 if (DEBUG_IMAGE) 510 debugLog('Finished:' . basename($imgfile)); 511 } else { 512 if (DEBUG_IMAGE) 513 debugLog('cacheImage: failed to create ' . $newfile); 514 imageError('404 Not Found', sprintf(gettext('cacheImage: failed to create %s'), $newfile), 'err-failimage.png', $imgfile, $album, $newfilename); 515 } 516 @chmod($newfile, FILE_MOD); 517 zp_imageKill($newim); 518 zp_imageKill($im); 519 } catch (Exception $e) { 520 debugLog('cacheImage(' . $newfilename . ') exception: ' . $e->getMessage()); 521 imageError('404 Not Found', sprintf(gettext('cacheImage(%1$s) exception: %2$s'), $newfilename, $e->getMessage()), 'err-failimage.png', $imgfile, $album, $newfilename); 522 return false; 523 } 524 clearstatcache(); 525 return true; 526} 527 528/* Determines the rotation of the image looking EXIF information. 529 * 530 * @param string $imgfile the image name 531 * @return false when the image should not be rotated, or the degrees the 532 * image should be rotated otherwise. 533 * 534 * PHP GD do not support flips so when a flip is needed we make a 535 * rotation that get close to that flip. But I don't think any camera will 536 * fill a flipped value in the tag. 537 */ 538function getImageRotation($imgfile) { 539 $rotation = false; 540 $imgfile_db = substr(filesystemToInternal($imgfile), strlen(ALBUM_FOLDER_SERVERPATH)); 541 $result = query_single_row('SELECT EXIFOrientation FROM ' . prefix('images') . ' AS i JOIN ' . prefix('albums') . ' as a ON i.albumid = a.id WHERE ' . db_quote($imgfile_db) . ' = CONCAT(a.folder,"/",i.filename)'); 542 if (is_null($result)) { 543 //try the file directly as this might be an image not in the database 544 if (in_array(getSuffix($imgfile), array('jpg', 'jpeg', 'tif', 'tiff'))) { 545 $result = exif_read_data($imgfile); 546 if (is_array($result) && array_key_exists('Orientation', $result)) { 547 $rotation = $result['Orientation']; 548 } 549 } 550 } else if (is_array($result) && array_key_exists('EXIFOrientation', $result)) { 551 $splits = preg_split('/!([(0-9)])/', $result['EXIFOrientation']); 552 $rotation = $splits[0]; 553 } 554 if ($rotation) { 555 switch ($rotation) { 556 case 1 : return false; // none 557 case 2 : return false; // mirrored 558 case 3 : return 180; // upside-down (not 180 but close) 559 case 4 : return 180; // upside-down mirrored 560 case 5 : return 270; // 90 CW mirrored (not 270 but close) 561 case 6 : return 270; // 90 CCW 562 case 7 : return 90; // 90 CCW mirrored (not 90 but close) 563 case 8 : return 90; // 90 CW 564 } 565 } 566 return false; 567} 568 569/** 570 * Adds a watermark to a resized image. If no watermark is set it just returns the image 571 * 572 * @since ZenphotoCMS 1.5.3 - consolidated from cacheImage() and full-image.php 573 * 574 * @param resource|object $newim GD image resource or Imagick object 575 * @param string $watermark_image The path to the watermark to use 576 * @param string $imgfile Path to the image being processed (optionally for debugging only) 577 * @return resource|object 578 */ 579function addWatermark($newim, $watermark_image, $imgfile = null) { 580 if ($watermark_image) { 581 $watermark = zp_imageGet($watermark_image); 582 if (!$watermark) { 583 imageError('404 Not Found', sprintf(gettext('Watermark %s not renderable.'), $watermark_image), 'err-failimage.png'); 584 } 585 $offset_h = getOption('watermark_h_offset') / 100; 586 $offset_w = getOption('watermark_w_offset') / 100; 587 $percent = getOption('watermark_scale') / 100; 588 $watermark_width = zp_imageWidth($watermark); 589 $watermark_height = zp_imageHeight($watermark); 590 $imw = zp_imageWidth($newim); 591 $imh = zp_imageHeight($newim); 592 $nw = sqrt(($imw * $imh * $percent) * ($watermark_width / $watermark_height)); 593 $nh = $nw * ($watermark_height / $watermark_width); 594 $r = sqrt(($imw * $imh * $percent) / ($watermark_width * $watermark_height)); 595 $r = min(1, $r); 596 $nw = round($watermark_width * $r); 597 $nh = round($watermark_height * $r); 598 $watermark_new = false; 599 if ($nw != $watermark_width || $nh != $watermark_height) { 600 $watermark_new = zp_imageResizeAlpha($watermark, $nw, $nh); 601 if (!zp_resampleImage($watermark_new, $watermark, 0, 0, 0, 0, $nw, $nh, $watermark_width, $watermark_height)) { 602 imageError('404 Not Found', sprintf(gettext('Watermark %s not resizeable.'), $watermark_image), 'err-failimage.png'); 603 } 604 } 605 606 // If upscaling is not allowed or it did not occur just use the original at least 607 if ($watermark_new === false) { 608 $watermark_new = $watermark; 609 } 610 // Position Overlay in Bottom Right 611 $dest_x = max(0, floor(($imw - $nw) * $offset_w)); 612 $dest_y = max(0, floor(($imh - $nh) * $offset_h)); 613 if (!is_null($imgfile) && DEBUG_IMAGE) { 614 debugLog("Watermark:" . basename($imgfile) . ": \$offset_h=$offset_h, \$offset_w=$offset_w, \$watermark_height=$watermark_height, \$watermark_width=$watermark_width, \$imw=$imw, \$imh=$imh, \$percent=$percent, \$r=$r, \$nw=$nw, \$nh=$nh, \$dest_x=$dest_x, \$dest_y=$dest_y"); 615 } 616 if (!zp_copyCanvas($newim, $watermark_new, $dest_x, $dest_y, 0, 0, $nw, $nh)) { 617 imageError('404 Not Found', sprintf(gettext('Image %s not renderable (copycanvas).'), filesystemToInternal($imgfile)), 'err-failimage.png', $imgfile, $album, $newfilename); 618 } 619 zp_imageKill($watermark); 620 /* 621 * GD special behaviour: 622 * If no resizing happened killing $watermark also already kills $watermark_new being the same 623 */ 624 if (GRAPHICS_LIBRARY != 'GD' || (GRAPHICS_LIBRARY == 'GD' && get_resource_type($watermark_new) == 'gd')) { 625 zp_imageKill($watermark_new); 626 } 627 } 628 return $newim; 629} 630