/** * Retrieve information for an image. * * @param string $image * The raw binary image data. * * @return mixed * FALSE is returned in case retrieving the image information 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 retrieving the information did work. * This array contains the following fields: * * - width : the width of the image in pixels * - height : the height of the image in pixels * - mime : the MIME type for the image * - type : one of the IMAGETYPE_XXX constants indicating the image type */ function phorum_api_image_info($image) { global $PHORUM; static $cache; if (!$cache) { $cache = array(); } // Return cached results when available. $cache_id = md5($image); if (isset($cache[$cache_id])) { return $cache[$cache_id]; } // 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(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. $tmpdir = $PHORUM['CACHECONFIG']['directory']; $tmpfile = $tmpdir . '/scale_image_tmp_' . $cache_id . microtime(TRUE); if (!phorum_api_write_file($tmpfile, $image)) { return FALSE; } // Get the image information and clean up the temporary file. $image_info = @getimagesize($tmpfile); @unlink($tmpfile); if ($image_info === FALSE) { return phorum_api_error(PHORUM_ERRNO_ERROR, 'Running getimagesize() on the image data failed'); } // We support only GIF, JPG and PNG images. if (substr($image_info['mime'], 0, 6) == 'image/') { if ($image_info[2] !== IMAGETYPE_JPEG && $image_info[2] !== IMAGETYPE_GIF && $image_info[2] !== IMAGETYPE_PNG) { return phorum_api_error(PHORUM_ERRNO_ERROR, "Scaling image type \"{$image_info['mime']}\" is not supported"); } } else { return phorum_api_error(PHORUM_ERRNO_ERROR, 'The file does not appear to be an image'); } $info = array('width' => $image_info[0], 'height' => $image_info[1], 'mime' => $image_info['mime'], 'type' => $image_info[2]); $cache[$cache_id] = $info; return $info; }
/** * 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'); }
/** * @deprecated Replaced by {@link phorum_api_write_file()}. */ function phorum_write_file($file, $data) { require_once PHORUM_PATH . '/include/api/write_file.php'; return phorum_api_write_file($file, $data); }
/** * Compile a Phorum template file into PHP code. * * Converts a Phorum template file into PHP code and writes the resulting code * to disk. This is the only call from include/api/template/compile.php * that is called from outside the file. All other functions are used * internally by the template compiling process. * * @param string $page * The template name (as used for {@link phorum_api_template()}). * * @param string $infile * The template input file to process. * * @param string $outfile * The file to write the resulting PHP code to. */ function phorum_api_template_compile($page, $infile, $outfile) { global $PHORUM; // Some backward compatibility for renamed template files. // Fall back to the deprecated template file if the new one is // not available. foreach ($PHORUM['API']['template_deprecated_files'] as $old => $new) { if ($page == $new && !file_exists($infile)) { // Rewrite the infile using the old template name. list($old, $phpfile, $infile) = phorum_api_template_resolve($old); // Just in case a .php file was used, in which case $infile // will be NULL. We treat that .php file as a .tpl file here. if (!$infile) { $infile = $phpfile; } } } // Template pass 1: // Recursively process all template {include ...} statements, to // construct a single template data block. list($template, $dependencies) = phorum_api_template_compile_pass1($infile); // Template pass 2: // Translate all other template statements into PHP code. $template = phorum_api_template_compile_pass2($template); // Write the compiled template to disk. // // For storing the compiled template, we use two files. The first one // has some code for checking if one of the dependent files has been // updated and for rebuilding the template if this is the case. // This one loads the second file, which is the compiled template itself. // // This two-stage loading is needed to make sure that syntax // errors in a template file won't break the depancy checking process. // If both were in the same file, the complete file would not be run // at all and the user would have to clean out the template cache to // reload the template once it was fixed. This way user intervention // is never needed. $stage1file = $outfile; $stage2file = $outfile . "-stage2"; $qstage1file = addslashes($stage1file); $qstage2file = addslashes($stage2file); // Output file for stage 1. This file contains code to check the file // dependencies. If one of the files that the template depends on is // changed, the template has to be rebuilt. Also rebuild in case the // second stage compiled template is missing. $checks = array(); $checks[] = "!file_exists(\"{$qstage2file}\")"; foreach ($dependencies as $file => $mtime) { $qfile = addslashes($file); $checks[] = "@filemtime(\"{$qfile}\") != {$mtime}"; } $qpage = addslashes($page); $stage1 = "<?php\n if (" . implode(" || ", $checks) . ") {\n @unlink (\"{$qstage1file}\");\n include phorum_api_template(\"{$qpage}\");\n return;\n } else {\n include \"{$qstage2file}\";\n }\n ?>"; phorum_api_write_file($stage1file, $stage1); // Output file for stage 2. This file contains the compiled template. phorum_api_write_file($stage2file, $template); }