startFromFile('image.jpg') * ->crop(100, 50) * ->width(50) * ->save('/path', 'newfile.'.$image->extension) * ->display() * ->end(); * * @author Éber Freitas Dias * @license http://www.gnu.org/copyleft/lgpl.html GNU Lesser General Public License 3 * * @package EasyGD * @version 1.0 - April 3rd, 2009 - 11:00am **/ class EasyGD { /******************************/ /* GLOBALS FOR ERROR HANDLING */ /******************************/ const ERROR_OPEN = "It wasn't possible to open the provided image."; const ERROR_SUPPORT = "This file format is not suported by EasyGD. Use a jpg or png image instead."; const ERROR_RESOURCE = "There is no resource for this function. Please, run EasyGD::startFrom* to define one."; const ERROR_CROP = "One of the dimensions provided are bigger than the actual image."; const ERROR_ARGUMENTS = "The function was called with missing arguments."; const ERROR_SERVER = "The server doesn't support this function. Please, contact you server admin."; const ERROR_QUALITY = "The quality value is out of boundaries."; /**********************/ /* GENERAL ATTRIBUTES */ /**********************/ /** * @var array List of supported extensions **/ private $ext2mime = array( 'jpg' => 'image/jpeg', 'png' => 'image/png' ); /** * @var array List of supported mime types **/ private $mime2ext = array( 'image/jpeg' => 'jpg', 'image/pjpeg' => 'jpg', 'image/png' => 'png' ); /** * @var array List of positions for cropping. The first letter represents the x axis (Left, Center, Right) and the second letter represents the y axis (Top, Center, Bottom) **/ private $cropTypes = array( 'LT', 'CT', 'RT', 'LC', 'CC', 'RC', 'LB', 'CB', 'RB' ); /** * @var resource Variable where the current resource will be stored **/ private $resource; /** * @var array This variable holds a list of functions to be used by specific file types **/ private $func; /** * @var int This stores the image quality (for JPG, from 0 to 100) and compression (for PNG, from 0 to 9) **/ private $quality; /** * @var int Width of the image **/ public $x; /** * @var int Height of the image **/ public $y; /** * @var string Holds the original file name **/ public $filename; /** * @var string Holds the filename assigned by EasyGD::save **/ public $newFilename; /** * @var string Extension from the current image being manipulated **/ public $extension; /** * You can pass a file name / file array, a url or a resource. In each case, * check the refering function to get the right arguments: * * file name or file array => EasyGD::startFromFile * url => EasyGD::startFromUrl * resource => EasyGD::startFromResource * * You can start the class without any variables and create a new image latter with * the functions from above. * * @return mixed **/ public function __construct() { $argsnum = func_num_args(); $args = func_get_args(); if($argsnum > 0) { if(gettype($args[0]) == 'resource') { return $this->startFromResource($args[0], @$args[1], @$args[2]); } if(strpos($args[0], 'http://')!== false) { return $this->startFromUrl($args[0]); } if(is_string($args[0]) || is_array($args[0])) { return $this->startFromFile($args[0]); } } } /** * Terminates the resource and free up some memory **/ public function __destruct() { $this->reset(); } /*******************/ /* START FUNCTIONS */ /*******************/ /** * Use this function to start EasyGD from a file. * @param string|array $file This can receive a string with the filename Ex. 'image.jpg' or an array from the $_FILES global Ex. $_FILES['myfile'] **/ public function startFromFile($file) { if(!is_array($file)) { $ext = strtolower(substr(strrchr($file, "."), 1)); if(!file_exists($file)) { $this->error(self::ERROR_OPEN); return false; } if(!$this->checkSupport(null, $ext)) { $this->error(self::ERROR_SUPPORT); return false; } $this->filename = $file; $this->extension = $ext; } else { if($file['error'] != 0) { $this->error(self::ERROR_OPEN); } if(!$this->checkSupport($file['type'])) { $this->error(self::ERROR_SUPPORT); return false; } $this->filename = $file['name']; $this->extension = $this->mime2ext[$file['type']]; $file = $file['tmp_name']; } $this->func = $this->getFunction($this->extension); $this->resource = call_user_func($this->func['create'], $file); list($this->x, $this->y) = getimagesize($file); return $this; } /** * With this function you can start EasyGD from an already started resource * @param resource $resource The image resource to be used * @param string $ext The original image extension * @param string $filename The original filename, not exactly necessary **/ public function startFromResource($resource, $ext = null, $filename = null) { if(is_null($ext)) { $this->error(self::ERROR_ARGUMENTS); return false; } if(!$this->checkSupport(null, $ext)) { $this->error(self::ERROR_SUPPORT); return false; } $this->resource = $resource; $this->filename = !is_null($filename) ? $filename : ''; $this->extension = $ext; $this->x = imagesx($resource); $this->y = imagesy($resource); $this->func = $this->getFunction($this->extension); return $this; } /** * With this function you can start EasyGD to use a remote image from a URL. * @param string $url The URL of the remote image **/ public function startFromUrl($url) { $ext = strtolower(substr(strrchr($url, "."), 1)); if(!$this->checkSupport(null, $ext)) { $this->error(self::ERROR_SUPPORT); return false; } if(function_exists('file_get_contents')) { $img = file_get_contents($url); } else { if(function_exists('curl_init')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); $img = curl_exec($ch); curl_close($ch); } else { $this->error(self::ERROR_SERVER); return false; } } $this->resource = imagecreatefromstring($img) or $this->error(self::ERROR_OPEN); $this->filename = strtolower(substr(strrchr($url, "/"), 1)); $this->extension = $ext; $this->x = imagesx($this->resource); $this->y = imagesy($this->resource); $this->func = $this->getFunction($this->extension); return $this; } /******************/ /* OUTPUT METHODS */ /******************/ /** * This function displays the image on the browser directly **/ public function display() { if(!$this->checkResource()) { $this->error(self::ERROR_RESOURCE); return false; } header('Content-Type: '.$this->ext2mime[$this->extension]); if(call_user_func_array($this->func['display'], array($this->resource, null, $this->quality))) { return $this; } return false; } /** * This function saves the resource to an image file. You can set the path where the file * will be saved as well as the file name. If none is given, the image will be saved on * the current path and the file name will be the timestamp of the moment which is pretty handy. * @param string $path Param to hold the path where the image will be stored. It should finish with a "/". * @param string $filename The image name to be used. Ex. 'image.jpg'. Don't forget to insert the right extension. You can use EasyGD::extension to do that. **/ public function save($path = '', $filename = null) { if(!empty($path) && $path{strlen($path)-1} != "/") $path = $path . "/"; if(is_null($filename)) $filename = time().".".$this->extension; $this->newFilename = $filename; if(call_user_func_array($this->func['display'], array($this->resource, $path.$filename, $this->quality))) { return $this; } return false; } /*****************/ /* SETUP METHODS */ /*****************/ /** * This function sets the quality of the image. If you are using JPEG images, you have to enter a number * from 0 to 100 where the bigger the number, better the quality. With PNG images this number means compression * and you have to enter a number from 0 to 9 where the bigger the number, more compression it has, which means * less quality and a smaller file size. * @param int $quality The number to set the image quality. **/ public function setQuality($quality) { if(!$this->checkResource()) { $this->error(self::ERROR_RESOURCE); return false; } switch($this->extension) { case 'jpg': if($quality > 100 || $quality < 0) { $this->error(self::ERROR_QUALITY); return false; } break; case 'png': if($quality > 9 || $quality < 0) { $this->error(self::ERROR_QUALITY); return false; } break; } $this->quality = $quality; return $this; } /** * If you are using transparent PNG images you might want to activate its * alpha channel. That is what this function does. **/ public function setAlpha() { if($this->extension == 'png') { imagealphablending($this->resource, true); imagesavealpha($this->resource, true); } return $this; } /************************/ /* MANIPULATION METHODS */ /************************/ /** * Function to resize an image * @param int $width Width to be resized * @param int $height Height to be resized **/ public function resize($width, $height) { if(!$this->checkResource()) { $this->error(self::ERROR_RESOURCE); return false; } $newResource = imagecreatetruecolor($width, $height); imagecopyresampled($newResource, $this->resource, 0, 0, 0, 0, $width, $height, $this->x, $this->y); $this->startFromResource($newResource, $this->extension, $this->filename); return $this; } /** * Shortcut for the EasyGD::resize function where you can set a with and the height is calculated proportionally * @param int $width New image width to be resized **/ public function width($width) { return $this->resize($width, round(($this->y / $this->x) * $width)); } /** * Shortcut for the EasyGD::resize function where you can set a height and the width is calculated proportionally * @param int $height New image height to be resized **/ public function height($height) { return $this->resize(round($this->x / $this->y) * $height, $height); } /** * Function where you can crop an image. You can use the pre-defined patterns with $type * or define your own position with $x and $y * @param int $width Width of the crop * @param int $height Height of the crop * @param string $type One of the EasyGD::cropTypes * @param int $x The X axis to start the crop * @param int $y The Y axis to start the crop */ public function crop($width, $height, $type = 'CC', $x = null, $y = null) { if(!$this->checkResource()) { $this->error(self::ERROR_RESOURCE); return false; } if($width > $this->x || $height > $this->y) { $this->error(self::ERROR_CROP); } if(!in_array($type, $this->cropTypes) || is_null($type)) { $type = 'CC'; } $leftX = $this->x - $width; $leftY = $this->y - $height; if(is_null($x) || is_null($y)) { switch($type) { case "LT": $x = $y = 0; break; case "CT": $x = round($leftX / 2); $y = 0; break; case "RT": $x = $leftX; $y = 0; break; case "LC": $x = 0; $y = round($leftY / 2); break; case "CC": $x = round($leftX / 2); $y = round($leftY / 2); break; case "RC": $x = $leftX; $y = round($leftY / 2); break; case "LB": $x = 0; $y = $leftY; break; case "CB": $x = round($leftX / 2); $y = $leftY; break; case "RB": $x = $leftX; $y = $leftY; break; } } else { if($x + $width > $this->x) $x = $leftX; if($y + $height > $this->y) $y = $leftY; } $newResource = imagecreatetruecolor($width, $height); imagecopy($newResource, $this->resource, 0, 0, $x, $y, $this->x, $this->y); $this->startFromResource($newResource, $this->extension, $this->filename); return $this; } /** * This will destroy the current resource and reset the variables so you can start * it all over again! :) **/ public function end() { return $this->reset(); } /********************/ /* INTERNAL METHODS */ /********************/ private function reset() { if(!$this->checkResource()) { return true; } imagedestroy($this->resource); $this->extension = null; $this->x = null; $this->y = null; $this->func = null; $this->quality = null; return true; } private function checkResource() { return gettype($this->resource) == 'resource'; } private function checkSupport($mime = null, $ext = null) { if(!is_null($mime)) { $mimes = array_keys($this->mime2ext); return in_array($mime, $mimes); } elseif(!is_null($ext)) { return in_array($ext, $this->mime2ext); } return false; } private function getFunction($ext) { switch($ext) { case "jpg": $this->quality = 100; return array( 'create'=> 'imagecreatefromjpeg', 'display'=> 'imagejpeg' ); break; case "png": $this->quality = 0; return array( 'create' => 'imagecreatefrompng', 'display' => 'imagepng' ); break; default: $this->error(self::ERROR_SUPPORT); return false; } } private function error($msg) { $this->reset(); trigger_error($msg, E_USER_ERROR); } } ?>