├── README.md ├── composer.json ├── src └── PHPImage.php └── test ├── center.php ├── chainable.php ├── clone.php ├── examples ├── center.jpg ├── overlay.jpg ├── text.jpg ├── thumb_100x100.jpg ├── thumb_200x400.jpg ├── thumb_400x200.jpg └── thumb_400x400.jpg ├── font ├── Rubik-Regular.otf └── arial.ttf ├── img ├── benji.jpg └── paw.png ├── multiple-instances.php ├── overlay.php ├── resize.php ├── stroke.php ├── text-otf.php ├── text.php └── upscale.php /README.md: -------------------------------------------------------------------------------- 1 | # PHP Image 2 | 3 | ## What does it do? 4 | 5 | Wrapper for PHP's GD Library for easy image manipulation to resize, crop and draw images on top of each other preserving transparency, writing text with stroke and transparency and drawing shapes. 6 | 7 | ## Installation 8 | 9 | Place the PHP file on your server and include it in your script or download via [composer](https://getcomposer.org/) `require: "kus/php-image": "dev-master"`. 10 | 11 | # Usage 12 | 13 | ## Example 14 | 15 | ```ruby 16 | $bg = './img/benji.jpg'; 17 | $overlay = './img/paw.png'; 18 | $image = new PHPImage(); 19 | $image->setDimensionsFromImage($bg); 20 | $image->draw($bg); 21 | $image->draw($overlay, '50%', '75%'); 22 | $image->rectangle(40, 40, 120, 80, array(0, 0, 0), 0.5); 23 | $image->setFont('./font/arial.ttf'); 24 | $image->setTextColor(array(255, 255, 255)); 25 | $image->setStrokeWidth(1); 26 | $image->setStrokeColor(array(0, 0, 0)); 27 | $image->text('Hello World!', array('fontSize' => 12, 'x' => 50, 'y' => 50)); 28 | $image->text('This is a big sentence with width 200px', array( 29 | 'fontSize' => 60, // Desired starting font size 30 | 'x' => 300, 31 | 'y' => 0, 32 | 'width' => 200, 33 | 'height' => 50, 34 | 'alignHorizontal' => 'center', 35 | 'alignVertical' => 'center', 36 | 'debug' => true 37 | )); 38 | $image->text('This is a big sentence', array( 39 | 'fontSize' => 60, // Desired starting font size 40 | 'x' => 300, 41 | 'y' => 200, 42 | 'width' => 200, 43 | 'height' => 50, 44 | 'alignHorizontal' => 'center', 45 | 'alignVertical' => 'center', 46 | 'debug' => true 47 | )); 48 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit.', array('width' => 100, 'fontSize' => 8, 'x' => 50, 'y' => 70)); 49 | $image->rectangle(40, 140, 170, 160, array(0, 0, 0), 0.5); 50 | $image->textBox('Auto wrap and scale font size to multiline text box width and height bounds. Vestibulum venenatis risus scelerisque enim faucibus, ac pretium massa condimentum. Curabitur faucibus mi at convallis viverra. Integer nec finibus ligula, id hendrerit felis.', array( 51 | 'width' => 150, 52 | 'height' => 140, 53 | 'fontSize' => 16, // Desired starting font size 54 | 'x' => 50, 55 | 'y' => 150 56 | )); 57 | $image->show(); 58 | ``` 59 | 60 | ![Overlay and text](https://raw.github.com/kus/php-image/master/test/examples/overlay.jpg "Overlay and text") 61 | 62 | ## Chainable 63 | 64 | ```ruby 65 | (new PHPImage('./img/benji.jpg'))->resize(200, 200, 'C', true)->show(); 66 | ``` 67 | 68 | ## Batch resize with optional crop and upscale 69 | 70 | ```ruby 71 | $image = new PHPImage('./img/benji.jpg'); 72 | $image->batchResize('examples/thumb_%dx%d.jpg', array( 73 | array(400, 400, true, true), 74 | array(200, 400, true, true), 75 | array(400, 200, true, true), 76 | array(100, 100, true, true), 77 | )); 78 | $image->resize(100, 100, true, true)->show(); 79 | ``` 80 | 81 | ![400x400](https://raw.github.com/kus/php-image/master/test/examples/thumb_400x400.jpg "400x400") 82 | ![400x200](https://raw.github.com/kus/php-image/master/test/examples/thumb_400x200.jpg "400x200") 83 | ![200x400](https://raw.github.com/kus/php-image/master/test/examples/thumb_200x400.jpg "200x400") 84 | ![100x100](https://raw.github.com/kus/php-image/master/test/examples/thumb_100x100.jpg "100x100") 85 | 86 | ## Multiline text box with auto wrap and auto font scale to fit bounds 87 | 88 | ```ruby 89 | $image = new PHPImage(400, 400); 90 | $image->rectangle(0, 0, 100, 200, array(0, 0, 0), 0.5); 91 | $image->setFont('./font/arial.ttf'); 92 | $image->setTextColor(array(255, 255, 255)); 93 | $image->setStrokeWidth(1); 94 | $image->setStrokeColor(array(0, 0, 0)); 95 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam molestie tortor quam, at congue nibh imperdiet dapibus.', array( 96 | 'width' => 100, 97 | 'height' => 100, 98 | 'fontSize' => 16, // Desired starting font size 99 | 'x' => 50, 100 | 'y' => 200 101 | )); 102 | $image->show(); 103 | ``` 104 | 105 | ![Multiline auto fit text](https://raw.github.com/kus/php-image/master/test/examples/text.jpg "Multiline auto fit text") 106 | 107 | ![Multiline text alignment](https://raw.github.com/kus/php-image/master/test/examples/center.jpg "Multiline text alignment") 108 | 109 | ## Text box auto fit text (variable font size) 110 | 111 | ```ruby 112 | $image = new PHPImage(400, 400); 113 | $image->setFont('./font/arial.ttf'); 114 | $image->setTextColor(array(255, 255, 255)); 115 | $image->text('This is a big sentence', array( 116 | 'fontSize' => 60, // Desired starting font size 117 | 'x' => 0, 118 | 'y' => 0, 119 | 'width' => 400, 120 | 'height' => 200, 121 | 'alignHorizontal' => 'center', 122 | 'alignVertical' => 'center', 123 | 'debug' => true 124 | )); 125 | $image->text('BIG', array( 126 | 'fontSize' => 120, // Desired starting font size 127 | 'x' => 0, 128 | 'y' => 200, 129 | 'width' => 400, 130 | 'height' => 200, 131 | 'alignHorizontal' => 'center', 132 | 'alignVertical' => 'center', 133 | 'debug' => true 134 | )); 135 | $image->show(); 136 | ``` 137 | 138 | ## Copyright 139 | 140 | Copyright (c) 2015 Blake Kus [blakek.us](http://blakek.us) 141 | 142 | This plugin is dual licenced under MIT and GPL Version 2 licences. 143 | 144 | Permission is hereby granted, free of charge, to any person obtaining a copy of 145 | this software and associated documentation files (the "Software"), to deal in 146 | the Software without restriction, including without limitation the rights to 147 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 148 | of the Software, and to permit persons to whom the Software is furnished to do 149 | so, subject to the following conditions: 150 | 151 | The above copyright notice and this permission notice shall be included in all 152 | copies or substantial portions of the Software. 153 | 154 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 155 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 156 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 157 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 158 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 159 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 160 | SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kus/php-image", 3 | "description": "Wrapper for PHP's GD Library for easy image manipulation", 4 | "keywords": ["php", "image", "text", "gd"], 5 | "homepage": "http://blakek.us", 6 | "type": "library", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Blake Kus", 11 | "email": "blakekus@gmail.com", 12 | "homepage": "http://www.blakek.us" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.3.0", 17 | "ext-gd": "*" 18 | }, 19 | "autoload": { 20 | "files": [ 21 | "src/PHPImage.php" 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/PHPImage.php: -------------------------------------------------------------------------------- 1 | 10 | * @license MIT License (http://www.opensource.org/licenses/mit-license.php) 11 | * @copyright 2015 Blake Kus 12 | * 13 | * CHANGELOG: 14 | * version 0.6.1 2017-12-04 15 | * FIXED: Stroke losing colour on multiline text (Thanks @choice2rejoice) 16 | * 17 | * version 0.6 2017-10-26 18 | * UPDATE: Support text alignment for multiline text 19 | * ADD: Configurable line height 20 | * ADD: OTF font support (Thanks @thorerik) 21 | * 22 | * version 0.5 2015-01-02 23 | * ADD: textBox auto scale font to width and height requested by @rufinus https://github.com/kus/php-image/issues/3 24 | * 25 | * version 0.4 2014-02-27 26 | * ADD: Image support for image cloning (Thanks @chainat) 27 | * ADD: Support to use GD commands to manipulate image and then continue using library 28 | * UPDATE: Private to Protected so library is extendable 29 | * ADD: Rotate 30 | * 31 | * version 0.3 2014-02-12 32 | * ADD: Examples 33 | * ADD: Initialise image on class instantiation 34 | * ADD: Image resize with optional upscaling 35 | * ADD: Image batch resize 36 | * ADD: Image crop 37 | * ADD: Option to save as gif/jpg/png 38 | * ADD: Snapshot images 39 | * FIX: Error in `textBox` reported by elbaku https://github.com/kus/php-image/issues/1 40 | * 41 | * version 0.2 2013-05-23 42 | * Add support for remote images 43 | * Add error handling when reading/writing files 44 | * Add ability to draw text box and auto fit text and align text 45 | * 46 | * version 0.1 2013-04-15 47 | * Initial release 48 | */ 49 | 50 | class PHPImage { 51 | /** 52 | * Canvas resource 53 | * 54 | * @var resource 55 | */ 56 | protected $img; 57 | 58 | /** 59 | * Canvas resource 60 | * 61 | * @var resource 62 | */ 63 | protected $img_copy; 64 | 65 | /** 66 | * PNG Compression level: from 0 (no compression) to 9. 67 | * JPEG Compression level: from 0 to 100 (no compression). 68 | * 69 | * @var integer 70 | */ 71 | protected $quality = 90; 72 | 73 | /** 74 | * Global font file 75 | * 76 | * @var String 77 | */ 78 | protected $fontFile; 79 | 80 | /** 81 | * Global font size 82 | * 83 | * @var integer 84 | */ 85 | protected $fontSize = 12; 86 | 87 | /** 88 | * Global line height 89 | * 90 | * @var float 91 | */ 92 | protected $lineHeight = 1.25; 93 | 94 | /** 95 | * Global text vertical alignment 96 | * 97 | * @var String 98 | */ 99 | protected $alignVertical = 'top'; 100 | 101 | /** 102 | * Global text horizontal alignment 103 | * 104 | * @var String 105 | */ 106 | protected $alignHorizontal = 'left'; 107 | 108 | /** 109 | * Global font colour 110 | * 111 | * @var array 112 | */ 113 | protected $textColor = array(255, 255, 255); 114 | 115 | /** 116 | * Global text opacity 117 | * 118 | * @var float 119 | */ 120 | protected $textOpacity = 1; 121 | 122 | /** 123 | * Global text angle 124 | * 125 | * @var integer 126 | */ 127 | protected $textAngle = 0; 128 | 129 | /** 130 | * Global stroke width 131 | * 132 | * @var integer 133 | */ 134 | protected $strokeWidth = 0; 135 | 136 | /** 137 | * Global stroke colour 138 | * 139 | * @var array 140 | */ 141 | protected $strokeColor = array(0, 0, 0); 142 | 143 | /** 144 | * Canvas width 145 | * 146 | * @var integer 147 | */ 148 | protected $width; 149 | 150 | /** 151 | * Canvas height 152 | * 153 | * @var integer 154 | */ 155 | protected $height; 156 | 157 | /** 158 | * Image type 159 | * 160 | * @var integer 161 | */ 162 | protected $type; 163 | 164 | /** 165 | * Default folder mode to be used if folder structure needs to be created 166 | * 167 | * @var String 168 | */ 169 | protected $folderMode = 0755; 170 | 171 | /** 172 | * Initialise the image with a file path, or dimensions, or pass no dimensions and 173 | * use setDimensionsFromImage to set dimensions from another image. 174 | * 175 | * @param string|integer $mixed (optional) file or width 176 | * @param integer $height (optional) 177 | * @return $this 178 | */ 179 | public function __construct($mixed=null, $height=null){ 180 | //Check if GD extension is loaded 181 | if (!extension_loaded('gd') && !extension_loaded('gd2')) { 182 | $this->handleError('GD is not loaded'); 183 | return false; 184 | } 185 | if($mixed !== null && $height !== null){ 186 | $this->initialiseCanvas($mixed, $height); 187 | }else if($mixed !== null && is_string($mixed)){ 188 | $image = $this->setDimensionsFromImage($mixed); 189 | $image->draw($mixed); 190 | return $image; 191 | } 192 | } 193 | 194 | /** 195 | * Intialise the canvas 196 | * 197 | * @param integer $width 198 | * @param integer $height 199 | * @return $this 200 | */ 201 | protected function initialiseCanvas($width, $height, $resource='img'){ 202 | $this->width = $width; 203 | $this->height = $height; 204 | unset($this->$resource); 205 | $this->$resource = imagecreatetruecolor($this->width, $this->height); 206 | // Set the flag to save full alpha channel information 207 | imagesavealpha($this->$resource, true); 208 | // Turn off transparency blending (temporarily) 209 | imagealphablending($this->$resource, false); 210 | // Completely fill the background with transparent color 211 | imagefilledrectangle($this->$resource, 0, 0, $this->width, $this->height, imagecolorallocatealpha($this->$resource, 0, 0, 0, 127)); 212 | // Restore transparency blending 213 | imagealphablending($this->$resource, true); 214 | return $this; 215 | } 216 | 217 | /** 218 | * After we update the image run this function 219 | */ 220 | protected function afterUpdate(){ 221 | $this->shadowCopy(); 222 | } 223 | 224 | /** 225 | * Store a copy of the image to be used for clone 226 | */ 227 | protected function shadowCopy(){ 228 | $this->initialiseCanvas($this->width, $this->height, 'img_copy'); 229 | imagecopy($this->img_copy, $this->img, 0, 0, 0, 0, $this->width, $this->height); 230 | } 231 | 232 | /** 233 | * Enable cloning of images in their current state 234 | * 235 | * $one = clone $image; 236 | */ 237 | public function __clone(){ 238 | $this->initialiseCanvas($this->width, $this->height); 239 | imagecopy($this->img, $this->img_copy, 0, 0, 0, 0, $this->width, $this->height); 240 | } 241 | 242 | 243 | /** 244 | * Get image height 245 | * 246 | * @return int 247 | */ 248 | public function getHeight(){ 249 | return $this->height; 250 | } 251 | 252 | /** 253 | * Get image width 254 | * 255 | * @return int 256 | */ 257 | public function getWidth(){ 258 | return $this->width; 259 | } 260 | 261 | 262 | /** 263 | * Get image resource (used when using a raw gd command) 264 | * 265 | * @return resource 266 | */ 267 | public function getResource(){ 268 | return $this->img; 269 | } 270 | 271 | /** 272 | * Set image resource (after using a raw gd command) 273 | * 274 | * @param $resource 275 | * @return $this 276 | */ 277 | public function setResource($resource){ 278 | $this->img = $resource; 279 | $this->width = imagesx($resource); 280 | $this->height = imagesy($resource); 281 | return $this; 282 | } 283 | 284 | /** 285 | * Set image dimensions from an image source 286 | * 287 | * @param String $file 288 | * @return $this 289 | */ 290 | public function setDimensionsFromImage($file){ 291 | if($info = $this->getImageInfo($file, false)){ 292 | $this->initialiseCanvas($info->width, $info->height); 293 | return $this; 294 | } else { 295 | $this->handleError($file . ' is not readable!'); 296 | } 297 | } 298 | 299 | /** 300 | * Check if an image (remote or local) is a valid image and return type, width, height and image resource 301 | * 302 | * @param string $file 303 | * @param boolean $returnResource 304 | * @return \stdClass 305 | */ 306 | protected function getImageInfo($file, $returnResource=true){ 307 | if($file instanceof PHPIMage) { 308 | $img = $file->img; 309 | $type = $file->type; 310 | $width = $file->width; 311 | $height = $file->height; 312 | } elseif (preg_match('#^https?://#i', $file)) { 313 | $headers = get_headers($file, 1); 314 | if (is_array($headers['Content-Type'])) { 315 | // Some servers return an array of content types, Facebook does this 316 | $contenttype = $headers['Content-Type'][0]; 317 | } else { 318 | $contenttype = $headers['Content-Type']; 319 | } 320 | if (preg_match('#^image/(jpe?g|png|gif)$#i', $contenttype)) { 321 | switch(true){ 322 | case stripos($contenttype, 'jpeg') !== false: 323 | case stripos($contenttype, 'jpg') !== false: 324 | $img = imagecreatefromjpeg($file); 325 | $type = IMAGETYPE_JPEG; 326 | break; 327 | case stripos($contenttype, 'png') !== false: 328 | $img = imagecreatefrompng($file); 329 | $type = IMAGETYPE_PNG; 330 | break; 331 | case stripos($contenttype, 'gif') !== false: 332 | $img = imagecreatefromgif($file); 333 | $type = IMAGETYPE_GIF; 334 | break; 335 | default: 336 | return false; 337 | break; 338 | } 339 | $width = imagesx($img); 340 | $height = imagesy($img); 341 | if (!$returnResource) { 342 | imagedestroy($img); 343 | } 344 | } else { 345 | return false; 346 | } 347 | } elseif (is_readable($file)) { 348 | list($width, $height, $type) = getimagesize($file); 349 | switch($type){ 350 | case IMAGETYPE_GIF: 351 | if ($returnResource) { 352 | $img = imagecreatefromgif($file); 353 | } 354 | break; 355 | case IMAGETYPE_JPEG: 356 | if ($returnResource) { 357 | $img = imagecreatefromjpeg($file); 358 | } 359 | break; 360 | case IMAGETYPE_PNG: 361 | if ($returnResource) { 362 | $img = imagecreatefrompng($file); 363 | } 364 | break; 365 | default: 366 | return false; 367 | break; 368 | } 369 | } else { 370 | return false; 371 | } 372 | $info = new \stdClass(); 373 | $info->type = $type; 374 | if($this->type === null){ 375 | // Assuming the first image you use is the output image type you want 376 | $this->type = $type; 377 | } 378 | $info->width = $width; 379 | $info->height = $height; 380 | if ($returnResource) { 381 | $info->resource = $img; 382 | } 383 | return $info; 384 | } 385 | 386 | /** 387 | * Handle errors 388 | * 389 | * @param String $error 390 | * 391 | * @throws Exception 392 | */ 393 | protected function handleError($error){ 394 | throw new \Exception($error); 395 | } 396 | 397 | /** 398 | * Rotate image 399 | * 400 | * @param $angle 401 | * @param int $bgd_color 402 | * @param int $ignore_transparent 403 | * @return $this 404 | */ 405 | public function rotate($angle, $bgd_color=0, $ignore_transparent=0){ 406 | $this->img = imagerotate($this->img, $angle, 0); 407 | $this->afterUpdate(); 408 | return $this; 409 | } 410 | 411 | /** 412 | * Crop an image 413 | * 414 | * @param integer $x 415 | * @param integer $y 416 | * @param integer $width 417 | * @param integer $height 418 | * @return $this 419 | */ 420 | public function crop($x, $y, $width, $height){ 421 | $tmp = $this->img; 422 | $this->initialiseCanvas($width, $height); 423 | imagecopyresampled($this->img, $tmp, 0, 0, $x, $y, $width, $height, $width, $height); 424 | imagedestroy($tmp); 425 | $this->afterUpdate(); 426 | return $this; 427 | } 428 | 429 | /** 430 | * Batch resize and save 431 | * 432 | * Usage: $image->batchResize('/path/to/img/test_%dx%d.jpg', array(array(100, 100, 'C', true),array(50, 50))); 433 | * 434 | * Will result in two images being saved (test_100x100.jpg and test_50x50.jpg) with 100x100 being cropped to the center. 435 | * 436 | * @param String $path 437 | * @param array $dimensions Array of `resize` arguments to run and save ie: array(100, 100, true, true) 438 | * @return $this 439 | */ 440 | public function batchResize($path, $dimensions=array()){ 441 | if(is_array($dimensions) && count($dimensions) > 0){ 442 | $width = $this->width; 443 | $height = $this->height; 444 | $copy = imagecreatetruecolor($width, $height); 445 | imagecopy($copy, $this->img, 0, 0, 0, 0, $width, $height); 446 | foreach($dimensions as $args){ 447 | $this->initialiseCanvas($width, $height); 448 | imagecopy($this->img, $copy, 0, 0, 0, 0, $width, $height); 449 | call_user_func_array(array($this, 'resize'), $args); 450 | $this->save(sprintf($path, $args[0], $args[1])); 451 | } 452 | $this->initialiseCanvas($width, $height); 453 | imagecopy($this->img, $copy, 0, 0, 0, 0, $width, $height); 454 | imagedestroy($copy); 455 | } 456 | return $this; 457 | } 458 | 459 | /** 460 | * Resize image to desired dimensions. 461 | * 462 | * Optionally crop the image using the quadrant. 463 | * 464 | * This function attempts to get the image to as close to the provided dimensions as possible, and then crops the 465 | * remaining overflow using the quadrant to get the image to be the size specified. 466 | * 467 | * The quadrants available are Top, Bottom, Center (default if crop = true), Left, and Right: 468 | * 469 | * +---+---+---+ 470 | * | | T | | 471 | * +---+---+---+ 472 | * | L | C | R | 473 | * +---+---+---+ 474 | * | | B | | 475 | * +---+---+---+ 476 | * 477 | * @param integer $targetWidth 478 | * @param integer $targetHeight 479 | * @param boolean|String $crop T, B, C, L, R 480 | * @param boolean $upscale 481 | * @return $this 482 | */ 483 | public function resize($targetWidth, $targetHeight, $crop=false, $upscale=false){ 484 | $width = $this->width; 485 | $height = $this->height; 486 | $canvasWidth = $targetWidth; 487 | $canvasHeight = $targetHeight; 488 | $r = $width / $height; 489 | $x = 0; 490 | $y = 0; 491 | if ($crop !== false) { 492 | if($crop === true){ 493 | $crop = 'C'; 494 | } 495 | if ($targetWidth/$targetHeight > $r) { 496 | // crop top/bottom 497 | $newheight = intval($targetWidth/$r); 498 | $newwidth = $targetWidth; 499 | switch($crop){ 500 | case 'T': 501 | $y = 0; 502 | break; 503 | case 'B': 504 | $y = intval(($newheight - $targetHeight) * ($height / $newheight)); 505 | break; 506 | case 'C': 507 | default: 508 | $y = intval((($newheight - $targetHeight) / 2) * ($height / $newheight)); 509 | break; 510 | } 511 | } else { 512 | // crop sides 513 | $newwidth = intval($targetHeight*$r); 514 | $newheight = $targetHeight; 515 | switch($crop){ 516 | case 'L': 517 | $x = 0; 518 | break; 519 | case 'R': 520 | $x = intval(($newwidth - $targetWidth) * ($width / $newwidth)); 521 | break; 522 | case 'C': 523 | default: 524 | $x = intval((($newwidth - $targetWidth) / 2) * ($width / $newwidth)); 525 | break; 526 | } 527 | } 528 | if($upscale === false){ 529 | if($newwidth > $width){ 530 | $x = 0; 531 | $newwidth = $width; 532 | $canvasWidth = $newwidth; 533 | } 534 | if($newheight > $height){ 535 | $y = 0; 536 | $newheight = $height; 537 | $canvasHeight = $newheight; 538 | } 539 | } 540 | } else { 541 | if ($targetWidth/$targetHeight > $r) { 542 | $newwidth = intval($targetHeight*$r); 543 | $newheight = $targetHeight; 544 | } else { 545 | $newheight = intval($targetWidth/$r); 546 | $newwidth = $targetWidth; 547 | } 548 | if($upscale === false){ 549 | if($newwidth > $width){ 550 | $newwidth = $width; 551 | } 552 | if($newheight > $height){ 553 | $newheight = $height; 554 | } 555 | } 556 | $canvasWidth = $newwidth; 557 | $canvasHeight = $newheight; 558 | } 559 | $tmp = $this->img; 560 | $this->initialiseCanvas($canvasWidth, $canvasHeight); 561 | imagecopyresampled($this->img, $tmp, 0, 0, $x, $y, $newwidth, $newheight, $width, $height); 562 | imagedestroy($tmp); 563 | $this->afterUpdate(); 564 | return $this; 565 | } 566 | 567 | /** 568 | * Shows the resulting image and cleans up. 569 | */ 570 | public function show(){ 571 | $this->checkQuality(); 572 | switch($this->type){ 573 | case IMAGETYPE_GIF: 574 | header('Content-type: image/gif'); 575 | imagegif($this->img, null); 576 | break; 577 | case IMAGETYPE_PNG: 578 | header('Content-type: image/png'); 579 | imagepng($this->img, null, $this->quality); 580 | break; 581 | default: 582 | header('Content-type: image/jpeg'); 583 | imagejpeg($this->img, null, $this->quality); 584 | break; 585 | } 586 | $this->cleanup(); 587 | } 588 | 589 | /** 590 | * Cleanup 591 | */ 592 | public function cleanup(){ 593 | imagedestroy($this->img); 594 | } 595 | 596 | /** 597 | * Save the image 598 | * 599 | * @param String $path 600 | * @param boolean $show 601 | * @param boolean $destroy 602 | * @return $this 603 | */ 604 | public function save($path, $show=false, $destroy=true){ 605 | if (!is_writable(dirname($path))) { 606 | if (!mkdir(dirname($path), $this->folderMode, true)) { 607 | $this->handleError(dirname($path) . ' is not writable and failed to create directory structure!'); 608 | } 609 | } 610 | if (is_writable(dirname($path))) { 611 | $this->checkQuality(); 612 | switch($this->type){ 613 | case IMAGETYPE_GIF: 614 | imagegif($this->img, $path); 615 | break; 616 | case IMAGETYPE_PNG: 617 | imagepng($this->img, $path, $this->quality); 618 | break; 619 | default: 620 | imagejpeg($this->img, $path, $this->quality); 621 | break; 622 | } 623 | } else { 624 | $this->handleError(dirname($path) . ' is not writable!'); 625 | } 626 | if($show){ 627 | $this->show(); 628 | return; 629 | } 630 | if($destroy){ 631 | $this->cleanup(); 632 | }else{ 633 | return $this; 634 | } 635 | } 636 | 637 | /** 638 | * Save the image and return object to continue operations 639 | * 640 | * @param string $path 641 | * @return $this 642 | */ 643 | public function snapshot($path){ 644 | return $this->save($path, false, false); 645 | } 646 | 647 | /** 648 | * Save the image and show it 649 | * 650 | * @param string $path 651 | */ 652 | public function showAndSave($path){ 653 | $this->save($path, true); 654 | } 655 | 656 | /** 657 | * Draw a line 658 | * 659 | * @param integer $x1 660 | * @param integer $y1 661 | * @param integer $x2 662 | * @param integer $y2 663 | * @param array $colour 664 | * @param float $opacity 665 | * @param boolean $dashed 666 | * @return $this 667 | */ 668 | public function line($x1=0, $y1=0, $x2=100, $y2=100, $colour=array(0, 0, 0), $opacity=1.0, $dashed=false){ 669 | if($dashed === true){ 670 | imagedashedline($this->img, $x1, $y1, $x2, $y2, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 671 | }else{ 672 | imageline($this->img, $x1, $y1, $x2, $y2, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 673 | } 674 | $this->afterUpdate(); 675 | return $this; 676 | } 677 | 678 | /** 679 | * Draw a rectangle 680 | * 681 | * @param integer $x 682 | * @param integer $y 683 | * @param integer $width 684 | * @param integer $height 685 | * @param array $colour 686 | * @param float $opacity 687 | * @param boolean $outline 688 | * @see http://www.php.net/manual/en/function.imagefilledrectangle.php 689 | * @return $this 690 | */ 691 | public function rectangle($x=0, $y=0, $width=100, $height=50, $colour=array(0, 0, 0), $opacity=1.0, $outline=false){ 692 | if($outline === true){ 693 | imagerectangle($this->img, $x, $y, $x + $width, $y + $height, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 694 | }else{ 695 | imagefilledrectangle($this->img, $x, $y, $x + $width, $y + $height, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 696 | } 697 | $this->afterUpdate(); 698 | return $this; 699 | } 700 | 701 | /** 702 | * Draw a square 703 | * 704 | * @param integer $x 705 | * @param integer $y 706 | * @param integer $width 707 | * @param array $colour 708 | * @param float $opacity 709 | * @param boolean $outline 710 | * @see http://www.php.net/manual/en/function.imagefilledrectangle.php 711 | * @return $this 712 | */ 713 | public function square($x=0, $y=0, $width=100, $colour=array(0, 0, 0), $opacity=1.0, $outline=false){ 714 | return $this->rectangle($x, $y, $width, $width, $colour, $opacity, $outline); 715 | } 716 | 717 | /** 718 | * Draw an ellipse 719 | * 720 | * @param integer $x 721 | * @param integer $y 722 | * @param integer $width 723 | * @param integer $height 724 | * @param array $colour 725 | * @param float $opacity 726 | * @param boolean $outline 727 | * @see http://www.php.net/manual/en/function.imagefilledellipse.php 728 | * @return $this 729 | */ 730 | public function ellipse($x=0, $y=0, $width=100, $height=50, $colour=array(0, 0, 0), $opacity=1.0, $outline=false){ 731 | if($outline === true){ 732 | imageellipse($this->img, $x, $y, $width, $height, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 733 | }else{ 734 | imagefilledellipse($this->img, $x, $y, $width, $height, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 735 | } 736 | $this->afterUpdate(); 737 | return $this; 738 | } 739 | 740 | /** 741 | * Draw a circle 742 | * 743 | * @param integer $x 744 | * @param integer $y 745 | * @param integer $width 746 | * @param array $colour 747 | * @param float $opacity 748 | * @param boolean $outline 749 | * @see http://www.php.net/manual/en/function.imagefilledellipse.php 750 | * @return $this 751 | */ 752 | public function circle($x=0, $y=0, $width=100, $colour=array(0, 0, 0), $opacity=1.0, $outline=false){ 753 | return $this->ellipse($x, $y, $width, $width, $colour, $opacity, $outline); 754 | } 755 | 756 | /** 757 | * Draw a polygon 758 | * 759 | * @param array $points 760 | * @param array $colour 761 | * @param float $opacity 762 | * @param boolean $outline 763 | * @see http://www.php.net/manual/en/function.imagefilledpolygon.php 764 | * @return $this 765 | */ 766 | public function polygon($points=array(), $colour=array(0, 0, 0), $opacity=1.0, $outline=false){ 767 | if(count($points) > 0){ 768 | if($outline === true){ 769 | imagepolygon($this->img, $points, count($points) / 2, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 770 | }else{ 771 | imagefilledpolygon($this->img, $points, count($points) / 2, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 772 | } 773 | $this->afterUpdate(); 774 | } 775 | return $this; 776 | } 777 | 778 | /** 779 | * Draw an arc 780 | * 781 | * @param integer $x 782 | * @param integer $y 783 | * @param integer $width 784 | * @param integer $height 785 | * @param integer $start 786 | * @param integer $end 787 | * @param array $colour 788 | * @param float $opacity 789 | * @param boolean $outline 790 | * @see http://www.php.net/manual/en/function.imagefilledarc.php 791 | * @return $this 792 | */ 793 | public function arc($x=0, $y=0, $width=100, $height=50, $start=0, $end=180, $colour=array(0, 0, 0), $opacity=1.0, $outline=false){ 794 | if($outline === true){ 795 | imagearc($this->img, $x, $y, $width, $height, $start, $end, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127)); 796 | }else{ 797 | imagefilledarc($this->img, $x, $y, $width, $height, $start, $end, imagecolorallocatealpha($this->img, $colour[0], $colour[1], $colour[2], (1 - $opacity) * 127), IMG_ARC_PIE); 798 | } 799 | $this->afterUpdate(); 800 | return $this; 801 | } 802 | 803 | /** 804 | * Draw an image from file 805 | * 806 | * Accepts x/y properties from CSS background-position (left, center, right, top, bottom, percentage and pixels) 807 | * 808 | * @param String $file 809 | * @param String|integer $x 810 | * @param String|integer $y 811 | * @see http://www.php.net/manual/en/function.imagecopyresampled.php 812 | * @see http://www.w3schools.com/cssref/pr_background-position.asp 813 | * @return $this 814 | */ 815 | public function draw($file, $x='50%', $y='50%'){ 816 | if($info = $this->getImageInfo($file)){ 817 | $image = $info->resource; 818 | $width = $info->width; 819 | $height = $info->height; 820 | // Defaults if invalid values passed 821 | if(strpos($x, '%') === false && !is_numeric($x) && !in_array($x, array('left', 'center', 'right'))){ 822 | $x = '50%'; 823 | } 824 | if(strpos($y, '%') === false && !is_numeric($y) && !in_array($y, array('top', 'center', 'bottom'))){ 825 | $y = '50%'; 826 | } 827 | // If word passed, convert it to percentage 828 | switch($x){ 829 | case 'left': 830 | $x = '0%'; 831 | break; 832 | case 'center': 833 | $x = '50%'; 834 | break; 835 | case 'right': 836 | $x = '100%'; 837 | break; 838 | } 839 | switch($y){ 840 | case 'top': 841 | $y = '0%'; 842 | break; 843 | case 'center': 844 | $y = '50%'; 845 | break; 846 | case 'bottom': 847 | $y = '100%'; 848 | break; 849 | } 850 | // Work out offset 851 | if(strpos($x, '%') > -1){ 852 | $x = str_replace('%', '', $x); 853 | $x = ceil(($this->width - $width) * ($x / 100)); 854 | } 855 | if(strpos($y, '%') > -1){ 856 | $y = str_replace('%', '', $y); 857 | $y = ceil(($this->height - $height) * ($y / 100)); 858 | } 859 | // Draw image 860 | imagecopyresampled( 861 | $this->img, 862 | $image, 863 | $x, 864 | $y, 865 | 0, 866 | 0, 867 | $width, 868 | $height, 869 | $width, 870 | $height 871 | ); 872 | imagedestroy($image); 873 | $this->afterUpdate(); 874 | return $this; 875 | } else { 876 | $this->handleError($file . ' is not a valid image!'); 877 | } 878 | } 879 | 880 | /** 881 | * Draw text 882 | * 883 | * ### Options 884 | * 885 | * - integer $fontSize 886 | * - integer $x 887 | * - integer $y 888 | * - integer $angle 889 | * - integer $strokeWidth 890 | * - float $opacity 891 | * - array $fontColor 892 | * - array $strokeColor 893 | * - String $fontFile 894 | * 895 | * @param String $text 896 | * @param array $options 897 | * @see http://www.php.net/manual/en/function.imagefttext.php 898 | * @return $this 899 | */ 900 | public function text($text, $options=array()){ 901 | // Unset null values so they inherit defaults 902 | foreach($options as $k => $v){ 903 | if($options[$k] === null){ 904 | unset($options[$k]); 905 | } 906 | } 907 | $defaults = array( 908 | 'fontSize' => $this->fontSize, 909 | 'fontColor' => $this->textColor, 910 | 'opacity' => $this->textOpacity, 911 | 'x' => 0, 912 | 'y' => 0, 913 | 'width' => null, 914 | 'height' => null, 915 | 'alignHorizontal' => $this->alignHorizontal, 916 | 'alignVertical' => $this->alignVertical, 917 | 'angle' => $this->textAngle, 918 | 'strokeWidth' => $this->strokeWidth, 919 | 'strokeColor' => $this->strokeColor, 920 | 'fontFile' => $this->fontFile, 921 | 'autoFit' => true, 922 | 'debug' => false 923 | ); 924 | extract(array_merge($defaults, $options), EXTR_OVERWRITE); 925 | if($fontFile === null){ 926 | $this->handleError('No font file set!'); 927 | } 928 | if(is_int($width) && $autoFit){ 929 | $fontSize = $this->fitToWidth($fontSize, $angle, $fontFile, $text, $width); 930 | } 931 | $lines = explode("\n", $text); 932 | if($debug && is_int($width) && is_int($height)){ 933 | $this->rectangle($x, $y, $width, $height, array(0, 255, 255), 0.5); 934 | } 935 | $fontHeight = $this->getFontHeight($fontSize, $angle, $fontFile, $lines); 936 | $lineHeight = $fontHeight * $this->lineHeight; 937 | foreach($lines as $index => $line){ 938 | // Get Y offset as it 0 Y is the lower-left corner of the character 939 | $testbox = imageftbbox($fontSize, $angle, $fontFile, $line); 940 | $offsety = abs($testbox[7]); 941 | $offsetx = 0; 942 | $actualWidth = abs($testbox[6] - $testbox[4]); 943 | $actualHeight = $lineHeight; 944 | $lineY = $y + ($lineHeight * $index); 945 | // If text box align text 946 | if(is_int($width) || is_int($height)){ 947 | if(!is_int($width)){ 948 | $width = $actualWidth; 949 | } 950 | if(!is_int($height)){ 951 | $height = $actualHeight; 952 | } 953 | switch($alignHorizontal){ 954 | case 'center': 955 | $offsetx += (($width - $actualWidth) / 2); 956 | break; 957 | case 'right': 958 | $offsetx += ($width - $actualWidth); 959 | break; 960 | } 961 | switch($alignVertical){ 962 | case 'center': 963 | $offsety += (($height - ($lineHeight * count($lines))) / 2); 964 | break; 965 | case 'bottom': 966 | $offsety += ($height - ($lineHeight * count($lines))); 967 | break; 968 | } 969 | if($debug){ 970 | $this->rectangle($x + $offsetx, $lineY + $offsety - $fontHeight, $actualWidth, $lineHeight, array(rand(150, 255), rand(150, 255), rand(150, 255)), 0.5); 971 | } 972 | } 973 | // Draw stroke 974 | if($strokeWidth > 0){ 975 | $tmpStrokeColor = imagecolorallocatealpha($this->img, $strokeColor[0], $strokeColor[1], $strokeColor[2], (1 - $opacity) * 127); 976 | for($sx = ($x-abs($strokeWidth)); $sx <= ($x+abs($strokeWidth)); $sx++){ 977 | for($sy = ($lineY-abs($strokeWidth)); $sy <= ($lineY+abs($strokeWidth)); $sy++){ 978 | imagefttext($this->img, $fontSize, $angle, $sx + $offsetx, $sy + $offsety, $tmpStrokeColor, $fontFile, $line); 979 | } 980 | } 981 | } 982 | // Draw text 983 | imagefttext($this->img, $fontSize, $angle, $x + $offsetx, $lineY + $offsety, imagecolorallocatealpha($this->img, $fontColor[0], $fontColor[1], $fontColor[2], (1 - $opacity) * 127), $fontFile, $line); 984 | } 985 | $this->afterUpdate(); 986 | return $this; 987 | } 988 | 989 | /** 990 | * Reduce font size to fit to width 991 | * 992 | * @param integer $fontSize 993 | * @param integer $angle 994 | * @param String $fontFile 995 | * @param String $text 996 | * @param integer $width 997 | * @return integer 998 | */ 999 | protected function fitToWidth($fontSize, $angle, $fontFile, $text, $width){ 1000 | while($fontSize > 0){ 1001 | $testbox = imageftbbox($fontSize, $angle, $fontFile, $text); 1002 | $actualWidth = abs($testbox[6] - $testbox[4]); 1003 | if($actualWidth <= $width){ 1004 | return $fontSize; 1005 | }else{ 1006 | $fontSize--; 1007 | } 1008 | } 1009 | return $fontSize; 1010 | } 1011 | 1012 | /** 1013 | * Reduce font size to fit to width and height 1014 | * 1015 | * @param integer $fontSize 1016 | * @param integer $angle 1017 | * @param String $fontFile 1018 | * @param String $text 1019 | * @param integer $width 1020 | * @param integer $height 1021 | * @return integer 1022 | */ 1023 | protected function fitToBounds($fontSize, $angle, $fontFile, $text, $width, $height){ 1024 | while($fontSize > 0){ 1025 | $wrapped = $this->wrap($text, $width, $fontSize, $angle, $fontFile); 1026 | $testbox = imageftbbox($fontSize, $angle, $fontFile, $wrapped); 1027 | $actualHeight = abs($testbox[1] - $testbox[7]); 1028 | if($actualHeight <= $height){ 1029 | return $fontSize; 1030 | }else{ 1031 | $fontSize--; 1032 | } 1033 | } 1034 | return $fontSize; 1035 | } 1036 | 1037 | /** 1038 | * Get font height 1039 | * 1040 | * @param integer $fontSize 1041 | * @param integer $angle 1042 | * @param String $fontFile 1043 | * @param array lines 1044 | * @return integer 1045 | */ 1046 | protected function getFontHeight($fontSize, $angle, $fontFile, $lines){ 1047 | $height = 0; 1048 | foreach ($lines as $index => $line) { 1049 | $testbox = imageftbbox($fontSize, $angle, $fontFile, $line); 1050 | $actualHeight = abs($testbox[1] - $testbox[7]); 1051 | if ($actualHeight > $height) { 1052 | $height = $actualHeight; 1053 | } 1054 | } 1055 | return $height; 1056 | } 1057 | 1058 | /** 1059 | * Draw multi-line text box and auto wrap text 1060 | * 1061 | * @param String $text 1062 | * @param array $options 1063 | * @return $this 1064 | */ 1065 | public function textBox($text, $options=array()){ 1066 | $defaults = array( 1067 | 'fontSize' => $this->fontSize, 1068 | 'fontColor' => $this->textColor, 1069 | 'opacity' => $this->textOpacity, 1070 | 'x' => 0, 1071 | 'y' => 0, 1072 | 'width' => 100, 1073 | 'height' => null, 1074 | 'angle' => $this->textAngle, 1075 | 'strokeWidth' => $this->strokeWidth, 1076 | 'strokeColor' => $this->strokeColor, 1077 | 'fontFile' => $this->fontFile, 1078 | 'debug' => false 1079 | ); 1080 | extract(array_merge($defaults, $options), EXTR_OVERWRITE); 1081 | if ($height) { 1082 | $fontSize = $this->fitTobounds($fontSize, $angle, $fontFile, $text, $width, $height); 1083 | } 1084 | return $this->text($this->wrap($text, $width, $fontSize, $angle, $fontFile), array('fontSize' => $fontSize, 'x' => $x, 'y' => $y, 'width' => $width, 'height' => $height, 'angle' => $angle, 'strokeWidth' => $strokeWidth, 'opacity' => $opacity, 'fontColor' => $fontColor, 'strokeColor' => $strokeColor, 'fontFile' => $fontFile, 'debug' => $debug)); 1085 | } 1086 | 1087 | /** 1088 | * Helper to wrap text 1089 | * 1090 | * @param String $text 1091 | * @param integer $width 1092 | * @param integer $fontSize 1093 | * @param integer $angle 1094 | * @param String $fontFile 1095 | * @return String 1096 | */ 1097 | protected function wrap($text, $width=100, $fontSize=12, $angle=0, $fontFile=null){ 1098 | if($fontFile === null){ 1099 | $fontFile = $this->fontFile; 1100 | } 1101 | $ret = ""; 1102 | $arr = explode(' ', $text); 1103 | foreach ($arr as $word){ 1104 | $teststring = $ret . ' ' . $word; 1105 | $testbox = imageftbbox($fontSize, $angle, $fontFile, $teststring); 1106 | if ($testbox[2] > $width){ 1107 | $ret .= ($ret == "" ? "" : "\n") . $word; 1108 | } else { 1109 | $ret .= ($ret == "" ? "" : ' ') . $word; 1110 | } 1111 | } 1112 | return $ret; 1113 | } 1114 | 1115 | /** 1116 | * Check quality is correct before save 1117 | * 1118 | * @return $this 1119 | */ 1120 | public function checkQuality(){ 1121 | switch($this->type){ 1122 | case IMAGETYPE_JPEG: 1123 | if($this->quality > 100){ 1124 | $this->quality = 100; 1125 | } else if ($this->quality < 0) { 1126 | $this->quality = 0; 1127 | } else if ($this->quality >= 0 && $this->quality <= 100) { 1128 | 1129 | } else { 1130 | $this->quality = 75; 1131 | } 1132 | break; 1133 | case IMAGETYPE_PNG: 1134 | if($this->quality > 9){ 1135 | $this->quality = 9; 1136 | } else if ($this->quality < 0) { 1137 | $this->quality = 0; 1138 | } else if ($this->quality >= 0 && $this->quality <= 9) { 1139 | 1140 | } else { 1141 | $this->quality = 6; 1142 | } 1143 | break; 1144 | } 1145 | return $this; 1146 | } 1147 | 1148 | /** 1149 | * Set's global folder mode if folder structure needs to be created 1150 | * 1151 | * @param integer $mode 1152 | * @return $this 1153 | */ 1154 | public function setFolderMode($mode=0755){ 1155 | $this->folderMode = $mode; 1156 | return $this; 1157 | } 1158 | 1159 | /** 1160 | * Set's global text size 1161 | * 1162 | * @param integer $size 1163 | * @return $this 1164 | */ 1165 | public function setFontSize($size=12){ 1166 | $this->fontSize = $size; 1167 | return $this; 1168 | } 1169 | 1170 | /** 1171 | * Set's global line height 1172 | * 1173 | * @param float $lineHeight 1174 | * @return $this 1175 | */ 1176 | public function setLineHeight($lineHeight=1.25){ 1177 | $this->lineHeight = $lineHeight; 1178 | return $this; 1179 | } 1180 | 1181 | /** 1182 | * Set's global text vertical alignment 1183 | * 1184 | * @param String $align 1185 | * @return $this 1186 | */ 1187 | public function setAlignVertical($align='top'){ 1188 | $this->alignVertical = $align; 1189 | return $this; 1190 | } 1191 | 1192 | /** 1193 | * Set's global text horizontal alignment 1194 | * 1195 | * @param String $align 1196 | * @return $this 1197 | */ 1198 | public function setAlignHorizontal($align='left'){ 1199 | $this->alignHorizontal = $align; 1200 | return $this; 1201 | } 1202 | 1203 | /** 1204 | * Set's global text colour using RGB 1205 | * 1206 | * @param array $colour 1207 | * @return $this 1208 | */ 1209 | public function setTextColor($colour=array(255, 255, 255)){ 1210 | $this->textColor = $colour; 1211 | return $this; 1212 | } 1213 | 1214 | /** 1215 | * Set's global text angle 1216 | * 1217 | * @param integer $angle 1218 | * @return $this 1219 | */ 1220 | public function setTextAngle($angle=0){ 1221 | $this->textAngle = $angle; 1222 | return $this; 1223 | } 1224 | 1225 | /** 1226 | * Set's global text stroke 1227 | * 1228 | * @param integer $strokeWidth 1229 | * @return $this 1230 | */ 1231 | public function setStrokeWidth($strokeWidth=0){ 1232 | $this->strokeWidth = $strokeWidth; 1233 | return $this; 1234 | } 1235 | 1236 | /** 1237 | * Set's global text opacity 1238 | * 1239 | * @param float $opacity 1240 | * @return $this 1241 | */ 1242 | public function setTextOpacity($opacity=1.0){ 1243 | $this->textOpacity = $opacity; 1244 | return $this; 1245 | } 1246 | 1247 | /** 1248 | * Set's global stroke colour 1249 | * 1250 | * @param array $colour 1251 | * @return $this 1252 | */ 1253 | public function setStrokeColor($colour=array(0, 0, 0)){ 1254 | $this->strokeColor = $colour; 1255 | return $this; 1256 | } 1257 | 1258 | /** 1259 | * Set's global font file for text from .ttf font file (TrueType) 1260 | * 1261 | * @param string $fontFile 1262 | * @return $this 1263 | */ 1264 | public function setFont($fontFile){ 1265 | $this->fontFile = $fontFile; 1266 | return $this; 1267 | } 1268 | 1269 | /** 1270 | * Set's global quality for PNG output 1271 | * 1272 | * @param string $quality 1273 | * @return $this 1274 | */ 1275 | public function setQuality($quality){ 1276 | $this->quality = $quality; 1277 | return $this; 1278 | } 1279 | 1280 | /** 1281 | * Set's global output type 1282 | * 1283 | * @param String $type 1284 | * @param String $quality 1285 | * @return $this 1286 | */ 1287 | public function setOutput($type, $quality = null){ 1288 | switch(strtolower($type)){ 1289 | case 'gif': 1290 | $this->type = IMAGETYPE_GIF; 1291 | break; 1292 | case 'jpg': 1293 | $this->type = IMAGETYPE_JPEG; 1294 | break; 1295 | case 'png': 1296 | $this->type = IMAGETYPE_PNG; 1297 | break; 1298 | } 1299 | if($quality !== null){ 1300 | $this->setQuality($quality); 1301 | } 1302 | return $this; 1303 | } 1304 | } 1305 | -------------------------------------------------------------------------------- /test/center.php: -------------------------------------------------------------------------------- 1 | setDimensionsFromImage($bg); 9 | $image->draw($bg); 10 | $image->resize(486, 540, true, true); 11 | $image->draw($overlay); 12 | $image->setAlignHorizontal('center'); 13 | $image->setAlignVertical('center'); 14 | $image->setFont('./font/arial.ttf'); 15 | $image->setTextColor(array(255, 255, 255)); 16 | $image->setStrokeWidth(1); 17 | $image->setStrokeColor(array(0, 0, 0)); 18 | $gutter = 50; 19 | $image->rectangle($gutter, $gutter, $image->getWidth() - $gutter * 2, $image->getHeight() - $gutter * 2, array(255, 255, 255), 0.5); 20 | $image->textBox("This is the first line.\n\nThis is the second line, which is a bit longer and wrapped.\nThird\nFourth\n\nFifth with a gap", array( 21 | 'width' => $image->getWidth() - $gutter * 2, 22 | 'height' => $image->getHeight() - $gutter * 2, 23 | 'fontSize' => 16, 24 | 'x' => $gutter, 25 | 'y' => $gutter 26 | )); 27 | $image->show(); 28 | -------------------------------------------------------------------------------- /test/chainable.php: -------------------------------------------------------------------------------- 1 | resize(200, 200, 'C', true)->show(); -------------------------------------------------------------------------------- /test/clone.php: -------------------------------------------------------------------------------- 1 | setFont('./font/arial.ttf'); 7 | $one = clone $image; 8 | $two = clone $image; 9 | $one->resize(200, 100, true, true)->text('one', array('fontColor' => array(0, 255, 0)))->save('./examples/one.jpg'); 10 | $two->resize(100, 200, true, true)->text('two', array('fontColor' => array(255, 0, 0)))->save('./examples/two.jpg'); 11 | $three = clone $two; 12 | // We should see Two and Three written 13 | $three->resize(80, 160, true, true)->text('three', array('fontColor' => array(0, 0, 255)))->save('./examples/three.jpg'); 14 | 15 | // Example using raw GD command 16 | $rotated = imagerotate($image->getResource(), 90, 0); 17 | // Rotate returns a new image resource, so set the new one to the active one 18 | $image->setResource($rotated); 19 | 20 | $image->show(); -------------------------------------------------------------------------------- /test/examples/center.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/center.jpg -------------------------------------------------------------------------------- /test/examples/overlay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/overlay.jpg -------------------------------------------------------------------------------- /test/examples/text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/text.jpg -------------------------------------------------------------------------------- /test/examples/thumb_100x100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/thumb_100x100.jpg -------------------------------------------------------------------------------- /test/examples/thumb_200x400.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/thumb_200x400.jpg -------------------------------------------------------------------------------- /test/examples/thumb_400x200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/thumb_400x200.jpg -------------------------------------------------------------------------------- /test/examples/thumb_400x400.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/examples/thumb_400x400.jpg -------------------------------------------------------------------------------- /test/font/Rubik-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/font/Rubik-Regular.otf -------------------------------------------------------------------------------- /test/font/arial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/font/arial.ttf -------------------------------------------------------------------------------- /test/img/benji.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/img/benji.jpg -------------------------------------------------------------------------------- /test/img/paw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kus/php-image/da869e1dc54e1a39131e58246472971850c2893d/test/img/paw.png -------------------------------------------------------------------------------- /test/multiple-instances.php: -------------------------------------------------------------------------------- 1 | setDimensionsFromImage($overlay); 11 | $avatar->draw($overlay); 12 | $avatar->resize(100, 100, false, false); 13 | 14 | // Main image 15 | $image = new PHPImage(); 16 | $image->setDimensionsFromImage($bg); 17 | $image->draw($bg); 18 | $image->setFont('./font/arial.ttf'); 19 | $image->setTextColor(array(255, 255, 255)); 20 | $image->setStrokeWidth(1); 21 | $image->setStrokeColor(array(0, 0, 0)); 22 | $image->text('<- Resized image from other PHP Image instance', array('fontSize' => 12, 'x' => 170, 'y' => 500)); 23 | 24 | // Draw Avatar on main image 25 | $image->rectangle(50, 450, 100, 100, array(255, 255, 255), 0.25); 26 | $image->draw($avatar, 50, 450); 27 | 28 | $image->show(); 29 | -------------------------------------------------------------------------------- /test/overlay.php: -------------------------------------------------------------------------------- 1 | setDimensionsFromImage($bg); 9 | $image->draw($bg); 10 | $image->draw($overlay, '50%', '75%'); 11 | $image->rectangle(40, 40, 120, 80, array(0, 0, 0), 0.5); 12 | $image->setFont('./font/arial.ttf'); 13 | $image->setTextColor(array(255, 255, 255)); 14 | $image->setStrokeWidth(1); 15 | $image->setStrokeColor(array(0, 0, 0)); 16 | $image->text('Hello World!', array('fontSize' => 12, 'x' => 50, 'y' => 50)); 17 | $image->text('This is a big sentence with width 200px', array( 18 | 'fontSize' => 60, 19 | 'x' => 300, 20 | 'y' => 0, 21 | 'width' => 200, 22 | 'height' => 50, 23 | 'alignHorizontal' => 'center', 24 | 'alignVertical' => 'center', 25 | 'debug' => true 26 | )); 27 | $image->text('This is a big sentence', array( 28 | 'fontSize' => 60, 29 | 'x' => 300, 30 | 'y' => 200, 31 | 'width' => 200, 32 | 'height' => 50, 33 | 'alignHorizontal' => 'center', 34 | 'alignVertical' => 'center', 35 | 'debug' => true 36 | )); 37 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit.', array('width' => 100, 'fontSize' => 8, 'x' => 50, 'y' => 70)); 38 | $image->rectangle(40, 140, 170, 160, array(0, 0, 0), 0.5); 39 | $image->textBox('Auto wrap and scale font size to multiline text box width and height bounds. Vestibulum venenatis risus scelerisque enim faucibus, ac pretium massa condimentum. Curabitur faucibus mi at convallis viverra. Integer nec finibus ligula, id hendrerit felis.', array( 40 | 'width' => 150, 41 | 'height' => 140, 42 | 'fontSize' => 16, 43 | 'x' => 50, 44 | 'y' => 150 45 | )); 46 | $image->show(); -------------------------------------------------------------------------------- /test/resize.php: -------------------------------------------------------------------------------- 1 | batchResize('examples/thumb_%dx%d.jpg', array( 7 | array(400, 400, true, true), 8 | array(200, 400, true, true), 9 | array(400, 200, true, true), 10 | array(100, 100, true, true), 11 | )); 12 | $image->resize(100, 100, true, true)->show(); -------------------------------------------------------------------------------- /test/stroke.php: -------------------------------------------------------------------------------- 1 | rectangle(0, 0, 100, 200, array(0, 0, 0), 0.5); 7 | $image->setFont('./font/arial.ttf'); 8 | $image->setTextColor(array(255, 255, 255)); 9 | 10 | // $image->setStrokeWidth(1); 11 | // $image->setStrokeColor(array(255, 0, 0)); 12 | // $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam molestie tortor quam, at congue nibh imperdiet dapibus.', array( 13 | // 'width' => 150, 14 | // 'height' => 140, 15 | // 'fontSize' => 16, // Desired starting font size 16 | // 'x' => 50, 17 | // 'y' => 150 18 | // )); 19 | 20 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam molestie tortor quam, at congue nibh imperdiet dapibus.', array( 21 | 'width' => 150, 22 | 'height' => 140, 23 | 'fontSize' => 16, // Desired starting font size 24 | 'x' => 50, 25 | 'y' => 150, 26 | 'strokeWidth' => 1, 27 | 'strokeColor' => array(255, 0, 0) 28 | )); 29 | 30 | $image->show(); 31 | -------------------------------------------------------------------------------- /test/text-otf.php: -------------------------------------------------------------------------------- 1 | setDimensionsFromImage($bg); 8 | $image->draw($bg); 9 | $image->setFont('./font/Rubik-Regular.otf'); 10 | $image->setTextColor(array(255, 255, 255)); 11 | $image->setStrokeWidth(1); 12 | $image->setStrokeColor(array(0, 0, 0)); 13 | $image->rectangle(40, 40, 120, 120, array(0, 0, 0), 0.5); 14 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit.', array( 15 | 'width' => 100, 16 | 'height' => 100, 17 | 'fontSize' => 16, 18 | 'x' => 50, 19 | 'y' => 50 20 | )); 21 | $image->rectangle(40, 190, 120, 120, array(0, 0, 0), 0.5); 22 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam molestie tortor quam, at congue nibh imperdiet dapibus.', array( 23 | 'width' => 100, 24 | 'height' => 100, 25 | 'fontSize' => 16, 26 | 'x' => 50, 27 | 'y' => 200 28 | )); 29 | $image->rectangle(40, 340, 120, 120, array(0, 0, 0), 0.5); 30 | $image->textBox('Vestibulum venenatis risus scelerisque enim faucibus, ac pretium massa condimentum. Curabitur faucibus mi at convallis viverra. Integer nec finibus ligula, id hendrerit felis.', array( 31 | 'width' => 100, 32 | 'height' => 100, 33 | 'fontSize' => 16, 34 | 'x' => 50, 35 | 'y' => 350 36 | )); 37 | $image->rectangle(190, 40, 320, 200, array(255, 255, 255), 0.5); 38 | $image->setTextColor(array(0, 0, 0)); 39 | $image->setStrokeWidth(0); 40 | $image->textBox("Auto wrap with auto font scale based on width and height of bounding box:\n\n\$image->textBox('MULTILINE TEXT', array(\n 'width' => 100,\n 'height' => 100,\n 'fontSize' => 16, // Desired starting size\n 'x' => 50,\n 'y' => 50\n));", array( 41 | 'width' => 300, 42 | 'height' => 180, 43 | 'fontSize' => 10, 44 | 'x' => 200, 45 | 'y' => 50 46 | )); 47 | $image->show(); 48 | -------------------------------------------------------------------------------- /test/text.php: -------------------------------------------------------------------------------- 1 | setDimensionsFromImage($bg); 8 | $image->draw($bg); 9 | $image->setFont('./font/arial.ttf'); 10 | $image->setTextColor(array(255, 255, 255)); 11 | $image->setStrokeWidth(1); 12 | $image->setStrokeColor(array(0, 0, 0)); 13 | $image->rectangle(40, 40, 120, 120, array(0, 0, 0), 0.5); 14 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit.', array( 15 | 'width' => 100, 16 | 'height' => 100, 17 | 'fontSize' => 16, 18 | 'x' => 50, 19 | 'y' => 50 20 | )); 21 | $image->rectangle(40, 190, 120, 120, array(0, 0, 0), 0.5); 22 | $image->textBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam molestie tortor quam, at congue nibh imperdiet dapibus.', array( 23 | 'width' => 100, 24 | 'height' => 100, 25 | 'fontSize' => 16, 26 | 'x' => 50, 27 | 'y' => 200 28 | )); 29 | $image->rectangle(40, 340, 120, 120, array(0, 0, 0), 0.5); 30 | $image->textBox('Vestibulum venenatis risus scelerisque enim faucibus, ac pretium massa condimentum. Curabitur faucibus mi at convallis viverra. Integer nec finibus ligula, id hendrerit felis.', array( 31 | 'width' => 100, 32 | 'height' => 100, 33 | 'fontSize' => 16, 34 | 'x' => 50, 35 | 'y' => 350 36 | )); 37 | $image->rectangle(190, 40, 320, 200, array(255, 255, 255), 0.5); 38 | $image->setTextColor(array(0, 0, 0)); 39 | $image->setStrokeWidth(0); 40 | $image->textBox("Auto wrap with auto font scale based on width and height of bounding box:\n\n\$image->textBox('MULTILINE TEXT', array(\n 'width' => 100,\n 'height' => 100,\n 'fontSize' => 16, // Desired starting size\n 'x' => 50,\n 'y' => 50\n));", array( 41 | 'width' => 300, 42 | 'height' => 180, 43 | 'fontSize' => 10, 44 | 'x' => 200, 45 | 'y' => 50 46 | )); 47 | $image->show(); -------------------------------------------------------------------------------- /test/upscale.php: -------------------------------------------------------------------------------- 1 | setFont('./font/arial.ttf'); 7 | $originalWidth = $image->getWidth(); 8 | $originalHeight = $image->getHeight(); 9 | $image->resize(ceil($originalWidth * 1.5), ceil($originalHeight * 1.5), /*crop*/ true, /* upscale*/ true); 10 | $image->setTextColor(array(255, 255, 255)); 11 | $image->setStrokeWidth(1); 12 | $image->textBox("Old dimensions: " . $originalWidth . "x" . $originalHeight . "\nNew dimensions: " . $image->getWidth() . "x" . $image->getHeight(), array( 13 | 'width' => $originalWidth, 14 | 'height' => $originalHeight, 15 | 'fontSize' => 16, 16 | 'x' => 10, 17 | 'y' => 10 18 | )); 19 | $image->show(); 20 | --------------------------------------------------------------------------------