function phorum_cache_rmdir($path) { if (defined('EVENT_LOGGING')) { phorum_mod_event_logging_suspend(); } $stack[] = $path; $dirs[] = $path; while (count($stack)) { $path = array_shift($stack); $dir = opendir($path); while ($entry = readdir($dir)) { if (is_file($path . "/" . $entry)) { @unlink($path . "/" . $entry); } elseif (is_dir($path . "/" . $entry) && $entry != '.' && $entry != '..') { array_unshift($dirs, $path . "/" . $entry); $stack[] = $path . "/" . $entry; } } closedir($dir); } foreach ($dirs as $dir) { @rmdir($dir); } if (defined('EVENT_LOGGING')) { phorum_mod_event_logging_resume(); } return; }
/** * Create an image thumbnail. * * This function can be used to create a scaled down thumbnail version of * an image. You can specifiy a width and/or a height to which to scale * down the image. The aspect ratio will always be kept. If both a width * and height are provided, then the one which requires the largest scale * down will be leading. * * @param string $image * The raw binary image data. * * @param integer $max_w * The maximum allowed width for the image in pixels. * Use NULL or 0 (zero) to indicate that any width will do. * * @param integer $max_h * The maximum allowed height for the image in pixels. * Use NULL or 0 (zero) to indicate that any height will do. * * @param string $method * The method to use for scaling the image. By default, this function * will try to autodetect a working method. Providing a $method parameter * is mostly useful for debugging purposes. Available methods (in the * order in which they are probed in the code) are: * - gd: using the GD library (requires extension "gd") * - imagick: using the ImageMagick library (requires extension "imagick") * - convert: using the ImageMagick "convert" tool (requires the * ImageMagick package to be installed on the server, does not work * in combination with some PHP safety restrictions). * * @return mixed * NULL is returned in case creating the thumbnail failed. The function * {@link phorum_api_strerror()} can be used to retrieve information * about the error which occurred. * * An array is returned in case creating the thumbnail did work. * This array contains the following fields: * - image: The scaled down image. NULL if no scaling was needed. * - method: The method that was used to create the thumbnail. * - cur_w: The width of the original $image. * - cur_h: The height of the original $image. * - cur_mime: The MIME type of the original $image. * - new_w: The width of the scaled down image. * - new_h: The height of the scaled down image. * - new_mime: The MIME type of the scaled down image, */ function phorum_api_image_thumbnail($image, $max_w = NULL, $max_h = NULL, $method = NULL) { // Reset error storage. $GLOBALS['PHORUM']['API']['errno'] = NULL; $GLOBALS['PHORUM']['API']['error'] = NULL; $error = NULL; if (empty($max_w)) { $max_w = NULL; } if (empty($max_h)) { $max_h = NULL; } // Initialize the return array. $img = array('image' => NULL, 'new_mime' => NULL); // Check if PHP supports the getimagesize() function. I think it // always should, but let's check it to be sure. if (!function_exists('getimagesize')) { return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'Your PHP installation lacks "getimagesize()" support'); } // Try to determine the image type and size using the getimagesize() // PHP function. Unfortunately, this function requires a file on disk // to process. Therefore we create a temporary file in the Phorum cache // for doing this. require_once './include/api/write_file.php'; $tmpdir = $GLOBALS['PHORUM']['cache']; $tmpfile = $tmpdir . '/scale_image_tmp_' . md5($image . microtime()); if (!phorum_api_write_file($tmpfile, $image)) { return NULL; } // Get the image information and clean up the temporary file. $file_info = getimagesize($tmpfile); @unlink($tmpfile); if ($file_info === FALSE) { return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'Running getimagesize() on the image data failed'); } // Add the image data to the return array. $img['cur_w'] = $img['new_w'] = $file_info[0]; $img['cur_h'] = $img['new_h'] = $file_info[1]; $img['cur_mime'] = $img['new_mime'] = $file_info['mime']; // We support only GIF, JPG and PNG images. if (substr($img['cur_mime'], 0, 6) == 'image/') { $type = substr($img['cur_mime'], 6); if ($type != 'jpeg' && $type != 'gif' && $type != 'png') { return phorum_api_error_set(PHORUM_ERRNO_ERROR, "Scaling image type \"{$img['cur_mime']}\" is not supported"); } } else { return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'The file does not appear to be an image'); } // Check if scaling is required and if yes, what the new size should be. // First, determine width and height scale factors. $scale_w = NULL; $scale_h = NULL; // Need horizontal scaling? if ($max_w !== NULL && $max_w < $img['cur_w']) { $scale_w = $max_w / $img['cur_w']; } // Need vertical scaling? if ($max_h !== NULL && $max_h < $img['cur_h']) { $scale_h = $max_h / $img['cur_h']; } // No scaling needed, return. if ($scale_w === NULL && $scale_h === NULL) { return $img; } // The lowest scale factor wins. Compute the required image size. if ($scale_h === NULL || $scale_w !== NULL && $scale_w < $scale_h) { $img['new_w'] = $max_w; $img['new_h'] = floor($img['cur_h'] * $scale_w + 0.5); } else { $img['new_w'] = floor($img['cur_w'] * $scale_h + 0.5); $img['new_h'] = $max_h; } // ----------------------------------------------------------------- // Try to use the imagick library tools // ----------------------------------------------------------------- // First, try to load the imagick extension if it is not loaded yet. // This way we can make this work on systems where imagick is not built-in // or loaded from the PHP ini. if (($method === NULL || $method == 'imagick') && !extension_loaded('imagick')) { @dl('imagick.so'); } if (($method === NULL || $method == 'imagick') && extension_loaded('imagick') && class_exists('Imagick')) { $method = NULL; $imagick = new Imagick(); $imagick->readImageBlob($image); $imagick->thumbnailImage($img['new_w'], $img['new_h'], TRUE); $imagick->setFormat("jpg"); $img['image'] = $imagick->getimageblob(); $img['new_mime'] = 'image/' . $imagick->getFormat(); $img['method'] = 'imagick'; return $img; } // ----------------------------------------------------------------- // Try to use the GD library tools // ----------------------------------------------------------------- // First, try to load the GD extension if it is not loaded yet. // This way we can make this work on systems where gd is not built-in // or loaded from the PHP ini. if (($method === NULL || $method == 'gd') && !extension_loaded('gd')) { @dl('gd.so'); } if (($method === NULL || $method == 'gd') && extension_loaded('gd') && function_exists('gd_info')) { // We need gd_info() to check whether GD has the required // image support for the type of image that we are handling. $gd = gd_info(); // We need PNG support for the scaled down image. if (empty($gd['PNG Support'])) { $error = "GD: no PNG support available for creating thumbnail"; } elseif ($type == 'gif' && empty($gd['GIF Read Support']) || $type == 'jpeg' && empty($gd['JPG Support']) || $type == 'png' && empty($gd['PNG Support'])) { $error = "GD: no support available for image type \"{$type}\""; } else { // Create a GD image handler based on the image data. // imagecreatefromstring() spawns PHP warnings if the file cannot // be processed. We do not care to see that in the event logging, // so if event logging is loaded, we suspend it here. // Instead, we catch error output and try to mangle it into a // usable error message. if (defined('EVENT_LOGGING')) { phorum_mod_event_logging_suspend(); } ob_start(); $original = imagecreatefromstring($image); $error = ob_get_contents(); ob_end_clean(); $error = trim(preg_replace('!(^(?:\\w+:\\s*).*?:|in /.*$)!', '', trim($error))); if (defined('EVENT_LOGGING')) { phorum_mod_event_logging_resume(); } if (!$original) { if ($error == '') { $error = "GD: Cannot process the {$type} image using GD"; } else { $error = 'GD: ' . $error; } } else { // Create the scaled image. $scaled = imagecreatetruecolor($img['new_w'], $img['new_h']); //Retain transparency. $trans_idx = imagecolortransparent($original); if ($trans_idx >= 0) { $trans = imagecolorsforindex($original, $trans_idx); $idx = imagecolorallocate($scaled, $trans['red'], $trans['green'], $trans['blue']); imagefill($scaled, 0, 0, $idx); imagecolortransparent($scaled, $idx); } elseif ($type == 'png') { imagealphablending($scaled, FALSE); $trans = imagecolorallocatealpha($scaled, 0, 0, 0, 127); imagefill($scaled, 0, 0, $trans); imagesavealpha($scaled, TRUE); } // Scale the image. imagecopyresampled($scaled, $original, 0, 0, 0, 0, $img['new_w'], $img['new_h'], $img['cur_w'], $img['cur_h']); // Create the jpeg output data for the scaled image. ob_start(); imagejpeg($scaled); $image = ob_get_contents(); $size = ob_get_length(); ob_end_clean(); $img['image'] = $image; $img['new_mime'] = 'image/jpeg'; $img['method'] = 'gd'; return $img; } } } // ----------------------------------------------------------------- // Try to use the ImageMagick "convert" tool // ----------------------------------------------------------------- if ($method === NULL || $method == 'convert') { // Try to find the "convert" utility. // First, check if it is configured in the Phorum settings. $convert = NULL; if (isset($PHORUM['imagemagick_convert_path'])) { $path = $PHORUM['imagemagick_convert_path']; if (is_executable($path)) { $convert = $path; } } // Not found? Then simply use "convert" and hope that it // can be found in the OS' path. if ($convert === NULL) { $convert = 'convert'; } // Build the command line. $cmd = escapeshellcmd($convert) . ' ' . '- ' . '-thumbnail ' . $img['new_w'] . 'x' . $img['new_h'] . ' ' . '-write jpeg:- ' . '--'; // Otherwise I get: option requires an argument `-write' // Run the command. $descriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')); $process = proc_open($cmd, $descriptors, $pipes); if ($process == FALSE) { $error = 'Failed to run "convert".'; } // Feed convert the image data on STDIN. fwrite($pipes[0], $image); fclose($pipes[0]); // Read the scaled image from STDOUT. $scaled = stream_get_contents($pipes[1]); fclose($pipes[1]); // Read errors. $errors = trim(stream_get_contents($pipes[2])); fclose($pipes[2]); $exit = proc_close($process); if ($exit == 0) { $img['image'] = $scaled; $img['new_mime'] = 'image/jpeg'; $img['method'] = 'convert'; return $img; } // Some error occurred. if ($errors == '') { $error = 'Got exit code ' . $exit . ' from "convert".'; } else { $error = $errors; } } // ----------------------------------------------------------------- // Safety nets. // ----------------------------------------------------------------- // Return error if one was set. if ($error) { return phorum_api_error_set(PHORUM_ERRNO_ERROR, $error); } // Catch illegal methods if ($method !== NULL) { return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'Illegal scaling method: ' . $method); } // If we get here, then we were totally out of luck. return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'No working image scaling method found'); }
/** * Clip a part from an image and optionally, resize it. * * This function can be used to clip a part of an image. You can specifiy * width and/or a height to which to scale the clipped image. * * Below, you see an image of the way in which a clip is defined by the * function parameters. $clip_x, $clip_y, $clip_w and $clip_h. * <code> * +------------------------------------------+ * | ^ | * | | | * | clip_y | * | | | * | v | * | +-------------------+ ^ | * | | | | | * |<--clip_x-->| | | | * | | CLIPPED | | | * | | IMAGE | clip_h | * | | | | | * | | | | | * | +-------------------+ v | * | | * | <-------clip_w------> | * | | * +------------------------------------------+ * </code> * * @param string $image * The raw binary image data. * * @param integer $clip_x * The X-offset for the clip, where 0 indicates the left of the image. * @param integer $clip_y * The Y-offset for the clip, where 0 indicates the top of the image. * @param integer $clip_w * The width of the clip to take out of the image. * @param integer $clip_h * The height of the clip to take out of the image. * * @param integer $dst_w * The width for the created clip image in pixels. * Use NULL or 0 (zero) to indicate that the width should be * the same as the $clip_w parameter. * @param integer $dst_h * The height for the created clip image in pixels. * Use NULL or 0 (zero) to indicate that the height should be * the same as the $clip_h parameter. * * @param string $method * The method to use for scaling the image. By default, this function * will try to autodetect a working method. Providing a $method parameter * is mostly useful for debugging purposes. Available methods (in the * order in which they are probed in the code) are: * - imagick : using the ImageMagick library (requires extension "imagick") * - gd : using the GD library (requires extension "gd") * - convert : using the ImageMagick "convert" tool (requires the * ImageMagick package to be installed on the server and does * not work in combination with some PHP safety restrictions). * * @return mixed * FALSE is returned in case creating the clip image failed. The function * {@link phorum_api_error_message()} can be used to retrieve information * about the error that occurred. * * An array is returned in case creating the clip image did work. * This array contains the following fields: * - image : The scaled down image. NULL if no scaling was needed. * - method : The method that was used to create the thumbnail. * - cur_w : The width of the original $image. * - cur_h : The height of the original $image. * - cur_mime : The MIME type of the original $image. * - new_w : The width of the scaled down image. * - new_h : The height of the scaled down image. * - new_mime : The MIME type of the scaled down image, */ function phorum_api_image_clip($image, $clip_x = 0, $clip_y = 0, $clip_w = NULL, $clip_h = NULL, $dst_w = NULL, $dst_h = NULL, $method = NULL) { global $PHORUM; settype($clip_x, 'int'); settype($clip_y, 'int'); settype($clip_w, 'int'); settype($clip_h, 'int'); settype($dst_w, 'int'); settype($dst_h, 'int'); // Reset error storage. $PHORUM['API']['errno'] = NULL; $PHORUM['API']['error'] = NULL; $error = NULL; // Initialize the return array. $img = array('image' => NULL, 'new_mime' => NULL); // Retrieve image info. $image_info = phorum_api_image_info($image); if ($image_info === FALSE) { return FALSE; } // Derive a name for the image type. switch ($image_info['type']) { case IMAGETYPE_JPEG: $type = 'jpeg'; break; case IMAGETYPE_GIF: $type = 'gif'; break; case IMAGETYPE_PNG: $type = 'png'; break; default: $type = 'unknown'; // should not occur } // The clip width and height are inherited from the image // width and height, unless they are set. if (empty($clip_w)) { $clip_w = $image_info['width'] - $clip_x; } if (empty($clip_h)) { $clip_h = $image_info['height'] - $clip_y; } // The target image width and height are inherited from the clip // width and height, unless they are set. if (empty($dst_w)) { $dst_w = $clip_w; } if (empty($dst_h)) { $dst_h = $clip_h; } // Add the image data to the return array. $img['cur_w'] = $image_info['width']; $img['cur_h'] = $image_info['height']; $img['new_w'] = $dst_w; $img['new_h'] = $dst_h; $img['cur_mime'] = $img['new_mime'] = $image_info['mime']; // Check if the requested clip fits the source image size. if ($clip_x + $clip_w > $img['cur_w']) { return phorum_api_error(PHROM_ERRNO_ERROR, "The clip X offset {$clip_x} + clip width {$clip_w} exceeds " . "the source image width {$img['cur_w']}"); } if ($clip_y + $clip_h > $img['cur_h']) { return phorum_api_error(PHROM_ERRNO_ERROR, "The clip Y offset {$clip_y} + clip height {$clip_h} exceeds " . "the source image height {$img['cur_h']}"); } // ----------------------------------------------------------------- // Try to use the imagick library tools // ----------------------------------------------------------------- if (($method === NULL || $method == 'imagick') && extension_loaded('imagick') && class_exists('Imagick')) { $method = NULL; $imagick = new Imagick(); $imagick->readImageBlob($image); $imagick->flattenImages(); $tmp = new Imagick(); $tmp->newPseudoImage($img['cur_w'], $img['cur_h'], 'xc:white'); $tmp->compositeImage($imagick, imagick::COMPOSITE_OVER, 0, 0); $imagick = $tmp; $imagick->cropImage($clip_w, $clip_h, $clip_x, $clip_y); $imagick->thumbnailImage($dst_w, $dst_h, FALSE); $imagick->setImagePage($clip_w, $clip_h, 0, 0); $imagick->setFormat("jpg"); $img['image'] = $imagick->getImagesBlob(); $img['new_mime'] = 'image/jpeg'; $img['method'] = 'imagick'; return $img; } // ----------------------------------------------------------------- // Try to use the GD library tools // ----------------------------------------------------------------- if (($method === NULL || $method == 'gd') && extension_loaded('gd') && function_exists('gd_info')) { // We need gd_info() to check whether GD has the required // image support for the type of image that we are handling. $gd = gd_info(); // We always need JPEG support for the scaled down image. if (empty($gd['JPG Support']) && empty($gd['JPEG Support'])) { $error = "GD: no JPEG support available for processing images"; } elseif ($type == 'gif' && empty($gd['GIF Read Support']) || $type == 'jpeg' && (empty($gd['JPG Support']) && empty($gd['JPEG Support'])) || $type == 'png' && empty($gd['PNG Support'])) { $error = "GD: no support available for image type \"{$type}\""; } else { // Create a GD image handler based on the image data. // imagecreatefromstring() spawns PHP warnings if the file cannot // be processed. We do not care to see that in the event logging, // so if event logging is loaded, we suspend it here. // Instead, we catch error output and try to mangle it into a // usable error message. if (defined('EVENT_LOGGING')) { phorum_mod_event_logging_suspend(); } ob_start(); $original = @imagecreatefromstring($image); $error = ob_get_contents(); ob_end_clean(); $error = trim(preg_replace('!(^(?:\\w+:\\s*).*?:|in /.*$)!', '', trim($error))); if (defined('EVENT_LOGGING')) { phorum_mod_event_logging_resume(); } if (!$original) { if ($error == '') { $error = "GD: Cannot process the {$type} image using GD"; } else { $error = 'GD: ' . $error; } } else { // Create the scaled image. $scaled = imagecreatetruecolor($dst_w, $dst_h); // Fill the image to have a background for transparent pixels. $white = imagecolorallocate($scaled, 255, 255, 255); imagefill($scaled, 0, 0, $white); // Scale the image. imagecopyresampled($scaled, $original, 0, 0, $clip_x, $clip_y, $dst_w, $dst_h, $clip_w, $clip_h); // Create the jpeg output data for the scaled image. ob_start(); imagejpeg($scaled); $image = ob_get_contents(); $size = ob_get_length(); ob_end_clean(); imagedestroy($original); imagedestroy($scaled); $img['image'] = $image; $img['new_mime'] = 'image/jpeg'; $img['method'] = 'gd'; return $img; } } } // ----------------------------------------------------------------- // Try to use the ImageMagick "convert" tool // ----------------------------------------------------------------- if ($method === NULL || $method == 'convert') { // Try to find the "convert" utility. // First, check if it is configured in the Phorum settings. $convert = NULL; if (isset($PHORUM['imagemagick_convert_path'])) { $path = $PHORUM['imagemagick_convert_path']; if (is_executable($path)) { $convert = $path; } } // Not found? Then simply use "convert" and hope that it // can be found in the OS' path. if ($convert === NULL) { $convert = 'convert'; } // Build the command line. $cmd = escapeshellcmd($convert) . ' ' . '- ' . '-background white -flatten ' . "-crop {$clip_w}x{$clip_h}+{$clip_x}+{$clip_y} " . '+repage ' . '-thumbnail ' . $dst_w . 'x' . $dst_h . '\\! ' . 'jpeg:-'; // Run the command. $descriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')); $process = proc_open($cmd, $descriptors, $pipes); if ($process == FALSE) { $error = 'Failed to execute "convert".'; } else { // Feed convert the image data on STDIN. fwrite($pipes[0], $image); fclose($pipes[0]); // Read the scaled image from STDOUT. $scaled = stream_get_contents($pipes[1]); fclose($pipes[1]); // Read errors. $errors = trim(stream_get_contents($pipes[2])); fclose($pipes[2]); $exit = proc_close($process); if ($exit == 0) { $img['image'] = $scaled; $img['new_mime'] = 'image/jpeg'; $img['method'] = 'convert'; return $img; } // Some error occurred. if ($errors == '') { $error = 'Got exit code ' . $exit . ' from "convert".'; } else { $error = $errors; } } } // ----------------------------------------------------------------- // Safety nets. // ----------------------------------------------------------------- // Return error if one was set. if ($error) { return phorum_api_error(PHORUM_ERRNO_ERROR, $error); } // Catch illegal methods if ($method !== NULL) { return phorum_api_error(PHORUM_ERRNO_ERROR, 'Illegal scaling method: ' . $method); } // If we get here, then we were totally out of luck. return phorum_api_error(PHORUM_ERRNO_ERROR, 'No working image scaling method found'); }