* @license http://www.contenido.org/license/LIZENZ.txt * @link http://www.4fb.de * @link http://www.contenido.org * @since file available since contenido release <= 4.6 * * {@internal * created 2003-08-08 * modified 2008-06-25, Frederic Schneider, add security fix * * $Id$: * }} * */ if (!defined('CON_FRAMEWORK')) { die('Illegal call'); } /* Info: * This file contains Contenido Image API functions. * * If you are planning to add a function, please make sure that: * 1.) The function is in the correct place * 2.) The function is documented * 3.) The function makes sense and is generically usable * */ /** * capiImgScaleGetMD5CacheFile: Returns the MD5 Filename used * for caching. * * @return string Path to the resulting image */ function capiImgScaleGetMD5CacheFile($sImg, $iMaxX, $iMaxY, $bCrop, $bExpand) { if (!file_exists($sImg)) { return false; } $iFilesize = filesize($sImg); if (function_exists("md5_file")) { $sMD5 = md5(implode("", array( $sImg, md5_file($sImg), $iFilesize, $iMaxX, $iMaxY, $bCrop, $bExpand))); } else { $sMD5 = md5(implode("", array( $sImg, $iFilesize, $iMaxX, $iMaxY, $bCrop, $bExpand))); } return $sMD5; } /** * capiImgScaleLQ: Scales (or crops) an image. * If scaling, the aspect ratio is maintained. * * Returns the path to the scaled temporary image. * * Note that this function does some very poor caching; * it calculates an md5 hash out of the image plus the * maximum X and Y sizes, and uses that as the file name. * If the file is older than 10 minutes, regenerate it. * * @param string $img The path to the image (relative to the frontend) * @param int $maxX The maximum size in x-direction * @param int $maxY The maximum size in y-direction * @param boolean $crop If true, the image is cropped and not scaled. * @param boolean $expand If true, the image is expanded (e.g. really scaled). * If false, the image will only be made smaller. * @param int $cacheTime The number of minutes to cache the image, use 0 for unlimited * @param int $quality The quality of the output file * @param boolean $keepType If true and a png file is source, output file is also png * * @return string !!!URL!!! to the resulting image (http://... */ function capiImgScaleLQ($img, $maxX, $maxY, $crop = false, $expand = false, $cacheTime = 10, $quality = 75, $keepType = false) { global $cfgClient, $lang, $client; $filename = $img; $cacheTime = (int) $cacheTime; $quality = (int) $quality; if ($quality <= 0 || $quality > 100) { $quality = 75; } $filetype = substr($filename, strlen($filename) - 4, 4); $filesize = filesize($img); $md5 = capiImgScaleGetMD5CacheFile($img, $maxX, $maxY, $crop, $expand); // Create the target file names for web and server if ($keepType) { // Should we keep the file type? switch (strtolower($filetype)) { // Just using switch if someone likes to add other types case ".png": $cfileName = $md5 . ".png"; break; default: $cfileName = $md5 . ".jpg"; } } else { // No... use .jpg $cfileName = $md5 . ".jpg"; } $cacheFile = $cfgClient[$client]["path"]["frontend"] . "cache/" . $cfileName; $webFile = $cfgClient[$client]["path"]["htmlpath"] . "cache/" . $cfileName; // Check if the file exists. If it does, check if the file is valid. if (file_exists($cacheFile)) { if ($cacheTime == 0) { // Do not check expiration date return $webFile; } else if (!function_exists("md5_file")) { // TODO: Explain why this is still needed ... or remove it if ((filemtime($cacheFile) + (60 * $cacheTime)) < time()) { /* Cache time expired, unlink the file */ unlink($cacheFile); } else { /* Return the web file name */ return $webFile; } } else { return $webFile; } } /* Get out which file we have */ switch (strtolower($filetype)) { case ".gif": $function = "imagecreatefromgif"; break; case ".png": $function = "imagecreatefrompng"; break; case ".jpg": $function = "imagecreatefromjpeg"; break; case "jpeg": $function = "imagecreatefromjpeg"; break; default: return false; } if (function_exists($function)) { $imageHandle = @$function($filename); } /* If we can't open the image, return false */ if (!$imageHandle) { return false; } $x = imagesx($imageHandle); $y = imagesy($imageHandle); /* Calculate the aspect ratio */ $aspectXY = $x / $y; $aspectYX = $y / $x; if (($maxX / $x) < ($maxY / $y)) { $targetY = $y * ($maxX / $x); $targetX = round($maxX); // force wished height if ($targetY < $maxY) { $targetY = ceil($targetY); } else { $targetY = floor($targetY); } } else { $targetX = $x * ($maxY / $y); $targetY = round($maxY); // force wished width if ($targetX < $maxX) { $targetX = ceil($targetX); } else { $targetX = floor($targetX); } } if ($expand == false && (($targetX > $x) || ($targetY > $y))) { $targetX = $x; $targetY = $y; } $targetX = ($targetX != 0) ? $targetX : 1; $targetY = ($targetY != 0) ? $targetY : 1; /* Create the target image with the target size, resize it afterwards. */ if ($crop) { /* Create the target image with the max size, crop it afterwards. */ $targetImage = imagecreate($maxX, $maxY); imagecopy($targetImage, $imageHandle, 0, 0, 0, 0, $maxX, $maxY); } else { /* Create the target image with the target size, resize it afterwards. */ $targetImage = imagecreate($targetX, $targetY); imagecopyresized($targetImage, $imageHandle, 0, 0, 0, 0, $targetX, $targetY, $x, $y); } // Output the file if ($keepType) { switch (strtolower($filetype)) { case ".png": imagepng($targetImage, $cacheFile); // no quality option available break; default: imagejpeg($targetImage, $cacheFile, $quality); } } else { imagejpeg($targetImage, $cacheFile, $quality); } return ($webFile); } /** * capiImgScaleHQ: Scales (or crops) an image in high quality. * If scaling, the aspect ratio is maintained. * * Note: GDLib 2.x is required! * * Returns the path to the scaled temporary image. * * Note that this function does some very poor caching; * it calculates an md5 hash out of the image plus the * maximum X and Y sizes, and uses that as the file name. * If the file is older than the specified cache time, regenerate it. * * @param string $img The path to the image (relative to the frontend) * @param int $maxX The maximum size in x-direction * @param int $maxY The maximum size in y-direction * @param boolean $crop If true, the image is cropped and not scaled. * @param boolean $expand If true, the image is expanded (e.g. really scaled). * If false, the image will only be made smaller. * @param int $cacheTime The number of minutes to cache the image, use 0 for unlimited * @param int $quality The quality of the output file * @param boolean $keepType If true and a png file is source, output file is also png * * @return string !!!URL!!! to the resulting image (http://...) */ function capiImgScaleHQ($img, $maxX, $maxY, $crop = false, $expand = false, $cacheTime = 10, $quality = 75, $keepType = false) { global $cfgClient, $lang, $client; $filename = $img; $cacheTime = (int) $cacheTime; $quality = (int) $quality; if ($quality <= 0 || $quality > 100) { $quality = 75; } $filetype = substr($filename, strlen($filename) - 4, 4); $filesize = filesize($img); $md5 = capiImgScaleGetMD5CacheFile($img, $maxX, $maxY, $crop, $expand); /* Create the target file names for web and server */ if ($keepType) { // Should we keep the file type? switch (strtolower($filetype)) { // Just using switch if someone likes to add other types case ".png": $cfileName = $md5 . ".png"; break; default: $cfileName = $md5 . ".jpg"; } } else { // No... use .jpg $cfileName = $md5 . ".jpg"; } $cacheFile = $cfgClient[$client]["path"]["frontend"] . "cache/" . $cfileName; $webFile = $cfgClient[$client]["path"]["htmlpath"] . "cache/" . $cfileName; /* Check if the file exists. If it does, check if the file is valid. */ if (file_exists($cacheFile)) { if ($cacheTime == 0) { // Do not check expiration date return $webFile; } else if (!function_exists("md5_file")) { // TODO: Explain why this is still needed ... or remove it if ((filemtime($cacheFile) + (60 * $cacheTime)) < time()) { /* Cache time expired, unlink the file */ unlink($cacheFile); } else { /* Return the web file name */ return $webFile; } } else { return $webFile; } } /* Get out which file we have */ switch (strtolower($filetype)) { case ".gif": $function = "imagecreatefromgif"; break; case ".png": $function = "imagecreatefrompng"; break; case ".jpg": $function = "imagecreatefromjpeg"; break; case "jpeg": $function = "imagecreatefromjpeg"; break; default: return false; } if (function_exists($function)) { $imageHandle = @$function($filename); } /* If we can't open the image, return false */ if (!$imageHandle) { return false; } $x = imagesx($imageHandle); $y = imagesy($imageHandle); /* Calculate the aspect ratio */ $aspectXY = $x / $y; $aspectYX = $y / $x; if (($maxX / $x) < ($maxY / $y)) { $targetY = $y * ($maxX / $x); $targetX = round($maxX); // force wished height if ($targetY < $maxY) { $targetY = ceil($targetY); } else { $targetY = floor($targetY); } } else { $targetX = $x * ($maxY / $y); $targetY = round($maxY); // force wished width if ($targetX < $maxX) { $targetX = ceil($targetX); } else { $targetX = floor($targetX); } } if ($expand == false && (($targetX > $x) || ($targetY > $y))) { $targetX = $x; $targetY = $y; } $targetX = ($targetX != 0) ? $targetX : 1; $targetY = ($targetY != 0) ? $targetY : 1; /* Create the target image with the target size, resize it afterwards. */ if ($crop) { /* Create the target image with the max size, crop it afterwards. */ $targetImage = imagecreatetruecolor($maxX, $maxY); imagecopy($targetImage, $imageHandle, 0, 0, 0, 0, $maxX, $maxY); } else { /* Create the target image with the target size, resize it afterwards. */ $targetImage = imagecreatetruecolor($targetX, $targetY); imagecopyresampled($targetImage, $imageHandle, 0, 0, 0, 0, $targetX, $targetY, $x, $y); } // Output the file if ($keepType) { switch (strtolower($filetype)) { case ".png": imagepng($targetImage, $cacheFile); // no quality option available break; default: imagejpeg($targetImage, $cacheFile, $quality); } } else { imagejpeg($targetImage, $cacheFile, $quality); } return ($webFile); } /** * capiImgScaleImageMagick: Scales (or crops) an image using ImageMagick. * If scaling, the aspect ratio is maintained. * * Note: ImageMagick is required! * * Returns the path to the scaled temporary image. * * Note that this function does some very poor caching; * it calculates an md5 hash out of the image plus the * maximum X and Y sizes, and uses that as the file name. * If the file is older than the specified cache time, regenerate it. * * @param string $img The path to the image (relative to the frontend) * @param int $maxX The maximum size in x-direction * @param int $maxY The maximum size in y-direction * @param boolean $crop If true, the image is cropped and not scaled. * @param boolean $expand If true, the image is expanded (e.g. really scaled). * If false, the image will only be made smaller. * @param int $cacheTime The number of minutes to cache the image, use 0 for unlimited * @param int $quality The quality of the output file * @param boolean $keepType If true and a png file is source, output file is also png * * @return string !!!URL!!! to the resulting image (http://...) */ function capiImgScaleImageMagick($img, $maxX, $maxY, $crop = false, $expand = false, $cacheTime = 10, $quality = 75, $keepType = false) { global $cfgClient, $lang, $client; $filename = $img; $cacheTime = (int) $cacheTime; $quality = (int) $quality; if ($quality <= 0 || $quality > 100) { $quality = 75; } $filetype = substr($filename, strlen($filename) - 4, 4); $filesize = filesize($img); $md5 = capiImgScaleGetMD5CacheFile($img, $maxX, $maxY, $crop, $expand); /* Create the target file names for web and server */ if ($keepType) { // Should we keep the file type? switch (strtolower($filetype)) { // Just using switch if someone likes to add other types case ".png": $cfileName = $md5 . ".png"; break; default: $cfileName = $md5 . ".jpg"; } } else { // No... use .jpg $cfileName = $md5 . ".jpg"; } $cacheFile = $cfgClient[$client]["path"]["frontend"] . "cache/" . $cfileName; $webFile = $cfgClient[$client]["path"]["htmlpath"] . "cache/" . $cfileName; /* Check if the file exists. If it does, check if the file is valid. */ if (file_exists($cacheFile)) { if ($cacheTime == 0) { // Do not check expiration date return $webFile; } else if (!function_exists("md5_file")) { // TODO: Explain why this is still needed ... or remove it if ((filemtime($cacheFile) + (60 * $cacheTime)) < time()) { /* Cache time expired, unlink the file */ unlink($cacheFile); } else { /* Return the web file name */ return $webFile; } } else { return $webFile; } } list($x, $y) = @getimagesize($filename); if ($x == 0 || $y == 0) { return false; } /* Calculate the aspect ratio */ $aspectXY = $x / $y; $aspectYX = $y / $x; if (($maxX / $x) < ($maxY / $y)) { $targetY = $y * ($maxX / $x); $targetX = round($maxX); // force wished height if ($targetY < $maxY) { $targetY = ceil($targetY); } else { $targetY = floor($targetY); } } else { $targetX = $x * ($maxY / $y); $targetY = round($maxY); // force wished width if ($targetX < $maxX) { $targetX = ceil($targetX); } else { $targetX = floor($targetX); } } if ($expand == false && (($targetX > $x) || ($targetY > $y))) { $targetX = $x; $targetY = $y; } $targetX = ($targetX != 0) ? $targetX : 1; $targetY = ($targetY != 0) ? $targetY : 1; // if is animated gif resize first frame if ($filetype == ".gif") { if (isAnimGif($filename)) { $filename .= "[0]"; } } /* Try to execute convert */ $output = array(); $retVal = 0; if ($crop) { exec("convert -gravity center -quality " . $quality . " -crop {$maxX}x{$maxY}+1+1 \"$filename\" $cacheFile", $output, $retVal); } else { exec("convert -quality " . $quality . " -geometry {$targetX}x{$targetY} \"$filename\" $cacheFile", $output, $retVal); } if (!file_exists($cacheFile)) { return false; } else { return ($webFile); } } /** * check if gif is animated * * @param string file path * * @return boolean true (gif is animated)/ false (single frame gif) */ function isAnimGif($sFile) { $output = array(); $retval = 0; exec('identify ' . $sFile, $output, $retval); if (count($output) == 1) { return false; } return true; } /** * capiImgScale: Scales (or crops) an image. * If scaling, the aspect ratio is maintained. * * This function chooses the best method to scale, depending on * the system environment and/or the parameters. * * Returns the path to the scaled temporary image. * * Note that this function does some very poor caching; * it calculates an md5 hash out of the image plus the * maximum X and Y sizes, and uses that as the file name. * If the file is older than 10 minutes, regenerate it. * * @param string $img The path to the image (relative to the frontend) * @param int $maxX The maximum size in x-direction * @param int $maxY The maximum size in y-direction * @param boolean $crop If true, the image is cropped and not scaled. * @param boolean $expand If true, the image is expanded (e.g. really scaled). * If false, the image will only be made smaller. * @param int $cacheTime The number of minutes to cache the image, use 0 for unlimited * @param boolean $wantHQ If true, try to force high quality mode * @param int $quality The quality of the output file * @param boolean $keepType If true and a png file is source, output file is also png * * @return string !!!URL!!! to the resulting image (http://...) * * @return string Path to the resulting image */ function capiImgScale($img, $maxX, $maxY, $crop = false, $expand = false, $cacheTime = 10, $wantHQ = false, $quality = 75, $keepType = false) { global $client, $db, $cfg, $cfgClient; $deleteAfter = false; $sRelativeImg = str_replace($cfgClient[$client]["upl"]["path"], "", $img); if (is_dbfs($sRelativeImg)) { // This check should be faster than a file existance check $dbfs = new DBFSCollection; $file = basename($sRelativeImg); $dbfs->writeToFile($sRelativeImg, $cfgClient[$client]["path"]["frontend"] . "cache/" . $file); $img = $cfgClient[$client]["path"]["frontend"] . "cache/" . $file; $deleteAfter = true; } else if (!file_exists($img)) { /* Try with upload string */ if (file_exists($cfgClient[$client]["upl"]["path"] . $img) && !is_dir($cfgClient[$client]["upl"]["path"] . $img)) { $img = $cfgClient[$client]["upl"]["path"] . $img; } else { /* No, it's neither in the upload directory nor in the dbfs. return. */ return false; } } $filename = $img; $filetype = substr($filename, strlen($filename) - 4, 4); $mxdAvImgEditingPosibility = checkImageEditingPosibility(); switch ($mxdAvImgEditingPosibility) { case '1': // gd1 $method = 'gd1'; if (!function_exists('imagecreatefromgif') && $filetype == '.gif') { $method = 'failure'; } break; case '2': //gd2 $method = 'gd2'; if (!function_exists('imagecreatefromgif') && $filetype == '.gif') { $method = 'failure'; } break; case 'im': //imagemagick $method = 'im'; break; case '0': $method = 'failure'; break; default: $method = 'failure'; break; } switch ($method) { case 'gd1': $return = capiImgScaleLQ($img, $maxX, $maxY, $crop, $expand, $cacheTime, $quality, $keepType); break; case 'gd2': $return = capiImgScaleHQ($img, $maxX, $maxY, $crop, $expand, $cacheTime, $quality, $keepType); break; case 'im': $return = capiImgScaleImageMagick($img, $maxX, $maxY, $crop, $expand, $cacheTime, $quality, $keepType); break; case 'failure': $return = str_replace($cfgClient[$client]["path"]["frontend"], $cfgClient[$client]["path"]["htmlpath"], $img); break; } if ($deleteAfter == true) { unlink($img); } return $return; } /** * check possible image editing functionality * * return mixed information about installed image editing extensions/tools */ function checkImageEditingPosibility() { if (isImageMagickAvailable()) { return 'im'; } else { if (extension_loaded('gd')) { if (function_exists('gd_info')) { $arrGDInformations = gd_info(); if (preg_match('#([0-9\.])+#', $arrGDInformations['GD Version'], $strGDVersion)) { if ($strGDVersion[0] >= '2') { return '2'; } return '1'; } return '1'; } return '1'; } return '0'; } } ?>