├── .github └── workflows │ └── devel.yml ├── .php-cs-fixer.dist.php ├── LICENSE-MIT ├── README.md ├── composer.json └── src ├── Canvas.php ├── Figure.php ├── Image.php ├── Text.php └── Utils.php /.github/workflows/devel.yml: -------------------------------------------------------------------------------- 1 | name: devel 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: 14 | - ubuntu-20.04 15 | php: 16 | - 7.4 17 | - 8.0 18 | - 8.1 19 | dependency-version: 20 | - prefer-lowest 21 | - prefer-stable 22 | 23 | name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Setup PHP 30 | uses: shivammathur/setup-php@v2 31 | with: 32 | php-version: ${{ matrix.php }} 33 | tools: composer:v2 34 | coverage: none 35 | 36 | - name: Install dependencies 37 | run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress 38 | 39 | - name: Unit tests 40 | run: composer run-script test 41 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__ . DIRECTORY_SEPARATOR . 'tests') 5 | ->in(__DIR__ . DIRECTORY_SEPARATOR . 'src') 6 | ->append(['.php-cs-fixer.dist.php']); 7 | 8 | $rules = [ 9 | '@Symfony' => true, 10 | 'phpdoc_no_empty_return' => false, 11 | 'array_syntax' => ['syntax' => 'short'], 12 | 'yoda_style' => false, 13 | 'binary_operator_spaces' => [ 14 | 'operators' => [ 15 | '=>' => 'align', 16 | '=' => 'align', 17 | ], 18 | ], 19 | 'concat_space' => ['spacing' => 'one'], 20 | 'not_operator_with_space' => false, 21 | ]; 22 | 23 | $rules['increment_style'] = ['style' => 'post']; 24 | 25 | return (new PhpCsFixer\Config()) 26 | ->setUsingCache(true) 27 | ->setRules($rules) 28 | ->setFinder($finder); 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015-present Jose Quintana 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GImage 2 | 3 | [![Build Status](https://github.com/joseluisq/gimage/actions/workflows/devel.yml/badge.svg?branch=master)](https://github.com/joseluisq/gimage/actions/workflows/devel.yml) [![](https://poser.pugx.org/joseluisq/gimage/version)](https://packagist.org/packages/joseluisq/gimage) [![Latest Unstable Version](https://poser.pugx.org/joseluisq/gimage/v/unstable)](//packagist.org/packages/joseluisq/gimage) [![Total Downloads](https://poser.pugx.org/joseluisq/gimage/downloads)](https://packagist.org/packages/joseluisq/gimage) [![License](https://poser.pugx.org/joseluisq/gimage/license)](https://packagist.org/packages/joseluisq/gimage) 4 | 5 | > A PHP library for easy image handling. 🖼 6 | 7 | __GImage__ is a simple and small library based on [PHP Image Processing and GD](http://php.net/manual/en/book.image.php) for processing images easily. 8 | 9 | ![A simple presentation card with GImage](https://cloud.githubusercontent.com/assets/1700322/18941713/eed7fa34-85d8-11e6-8033-bf787e4aa236.png) 10 | 11 | _Presentation card built with GImage - [View code example](https://joseluisq.github.io/gimage/examples/creating-a-presentation-card/)_ 12 | 13 | ## Features 14 | 15 | - Load an image from a local path, URL or image resource. 16 | - Create shapes such as rectangles or ellipses with opacity. 17 | - Resize, scale or crop images proportionally. 18 | - Rotate images, shapes or texts. 19 | - Embed text with custom TTF fonts. 20 | - Compose a pool of images with `Canvas`. 21 | - Swap image formats such as JPEG, PNG or GIF. 22 | - Save images locally or output them on the browser. 23 | - Save several copies of the same image. 24 | - Render an image in memory and return its resource. 25 | 26 | ## Requirements 27 | 28 | GImage requires PHP [`7.4`](https://www.php.net/releases/7_4_0.php) or a recent PHP version with the latest [GD Extension](http://php.net/manual/en/book.image.php). 29 | 30 | ## Install 31 | 32 | ```sh 33 | composer require joseluisq/gimage 34 | ``` 35 | 36 | ## Usage 37 | 38 | Load an external PNG image and save it as JPG: 39 | 40 | ```php 41 | load($url) 52 | // Scale to 50% (300x99) 53 | ->scale(0.5) 54 | // Change the format to JPG 55 | ->toJPG() 56 | // Saving in local path 57 | ->save('arch.jpg'); 58 | ``` 59 | 60 | See [GImage Website](https://bit.ly/gimage-php) for detailed usage instructions and more advanced examples. 61 | 62 | ## Changelog 63 | 64 | Check out the [CHANGELOG.md](./CHANGELOG.md) file. 65 | 66 | ## Examples 67 | 68 | See [code examples](./examples) directory. 69 | 70 | ## Development 71 | 72 | ### Run tests 73 | 74 | Via [Composer](https://getcomposer.org/) 75 | 76 | ```sh 77 | composer install 78 | composer run-script test 79 | ``` 80 | 81 | Via [Docker](https://www.docker.com/) (multi PHP versions test) 82 | 83 | ```sh 84 | make docker-tests 85 | ``` 86 | 87 | ## Contribution 88 | 89 | Feel free to send some [Pull request](https://github.com/joseluisq/gimage/pulls) or file an [issue](https://github.com/joseluisq/gimage/issues). 90 | 91 | ## License 92 | 93 | This work is primarily distributed under the terms of the [MIT license](LICENSE-MIT). 94 | 95 | ©2015-present [Jose Quintana](https://joseluisq.net) 96 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joseluisq/gimage", 3 | "description": "A PHP library for easy image handling.", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": [ 7 | "image", 8 | "gd", 9 | "png", 10 | "jpg", 11 | "jpeg", 12 | "resize", 13 | "ellipse", 14 | "rectangle", 15 | "crop", 16 | "file", 17 | "save", 18 | "handler" 19 | ], 20 | "authors": [ 21 | { 22 | "name": "Jose Quintana", 23 | "homepage": "https://github.com/joseluisq", 24 | "role": "Owner" 25 | } 26 | ], 27 | "support": { 28 | "issues": "https://github.com/joseluisq/gimage/issues" 29 | }, 30 | "require": { 31 | "php": ">=7.4", 32 | "ext-gd": "*" 33 | }, 34 | "config": { 35 | "platform": { 36 | "php": "7.4.0" 37 | } 38 | }, 39 | "require-dev": { 40 | "phpunit/phpunit": "9", 41 | "friendsofphp/php-cs-fixer": "3" 42 | }, 43 | "autoload": { 44 | "psr-4": { 45 | "GImage\\": "src/" 46 | } 47 | }, 48 | "autoload-dev": { 49 | "psr-4": { 50 | "GImage\\Tests\\": "tests/" 51 | } 52 | }, 53 | "scripts": { 54 | "lint": "PHP_CS_FIXER_IGNORE_ENV=true vendor/bin/php-cs-fixer fix -v --allow-risky=yes --dry-run", 55 | "format": "PHP_CS_FIXER_IGNORE_ENV=true vendor/bin/php-cs-fixer fix -v --allow-risky=yes", 56 | "test:unit": "./vendor/bin/phpunit", 57 | "test": [ 58 | "@lint", 59 | "@test:unit" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Canvas.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace GImage; 12 | 13 | /** 14 | * A Canvas represents a rectangular image area on which one can append images, text and figures. 15 | * 16 | * @author Jose Quintana 17 | * 18 | * @property array $elementList An array of elements (Image, Figure or Text classes). 19 | */ 20 | class Canvas extends Image 21 | { 22 | /** 23 | * List of elements. 24 | * 25 | * @var array 26 | */ 27 | private $elementList = []; 28 | 29 | /** 30 | * Constructs a new Canvas. 31 | * 32 | * @param mixed $element only Image or Figure classes 33 | */ 34 | public function __construct($element = null) 35 | { 36 | parent::__construct($element); 37 | } 38 | 39 | /** 40 | * Adds one or more elements to canvas. 41 | * 42 | * @param mixed $elements single or array of Image, Figure, Text classes 43 | * 44 | * @return \GImage\Canvas|static 45 | */ 46 | public function append($elements) 47 | { 48 | if (!empty($elements)) { 49 | $elements = is_array($elements) ? $elements : [$elements]; 50 | 51 | foreach ($elements as $element) { 52 | if ($element instanceof Image || $element instanceof Text) { 53 | $this->elementList[] = $element; 54 | } 55 | } 56 | } 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Draws the canvas. 63 | * 64 | * @return \GImage\Canvas|static 65 | * 66 | * @throws \Exception 67 | */ 68 | public function draw() 69 | { 70 | $canvas = $this->resource; 71 | 72 | if ($canvas) { 73 | foreach ($this->elementList as $element) { 74 | if ($element instanceof Image) { 75 | $this->drawImage($element, $canvas); 76 | } 77 | 78 | if ($element instanceof Text) { 79 | $this->drawText($element, $canvas); 80 | } 81 | } 82 | 83 | $this->resource = $canvas; 84 | } else { 85 | throw new \Exception('' . 'Image or Figure class is not assigned. ' . 'E.g. "new Canvas($image_or_figure)"' . ''); 86 | } 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * Draw the an Image or Figure element. 93 | * 94 | * @param \GImage\Image|\GImage\Figure $element image or Figure element 95 | * @param mixed $canvas 96 | * 97 | * @return void 98 | */ 99 | private function drawImage($element, $canvas) 100 | { 101 | $image = $element->getResource(); 102 | 103 | imagecopyresampled( 104 | $canvas, 105 | $image, 106 | $element->getLeft(), 107 | $element->getTop(), 108 | $element->getBoxLeft(), 109 | $element->getBoxTop(), 110 | $element->getBoxWidth(), 111 | $element->getBoxHeight(), 112 | $element->getWidth(), 113 | $element->getHeight() 114 | ); 115 | } 116 | 117 | /** 118 | * Draw a Text element. 119 | * 120 | * @param \GImage\Text $text text element 121 | * @param mixed $canvas 122 | * 123 | * @return void 124 | */ 125 | private function drawText(Text $text, $canvas) 126 | { 127 | list($red, $green, $blue) = $text->getColor(); 128 | $opacity = Utils::fixPNGOpacity($text->getOpacity()); 129 | 130 | $color = imagecolorallocatealpha( 131 | $canvas, 132 | $red, 133 | $green, 134 | $blue, 135 | $opacity < 127 ? $opacity : 0 136 | ); 137 | 138 | $size = $text->getSize(); 139 | $angle = $text->getAngle(); 140 | $font = $text->getFontface(); 141 | $lineHeight = $text->getLineHeight() * $text->getSize(); 142 | 143 | list($x, $y) = $text->getCords(); 144 | $x = (int) floor($x); 145 | 146 | $lines = $text->wrappText(); 147 | 148 | foreach ($lines as $i => $line) { 149 | $ny = (int) floor($y + ($lineHeight * $i)); 150 | imagealphablending($canvas, true); 151 | imagettftext($canvas, $size, $angle, $x, $ny, $color, $font, $line); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Figure.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace GImage; 12 | 13 | /** 14 | * Class to embed simple graphic into the Canvas. 15 | * 16 | * @author Jose Quintana 17 | * 18 | * @property int $red Red color 19 | * @property int $green Green color 20 | * @property int $blue Blue color 21 | */ 22 | class Figure extends Image 23 | { 24 | protected $height = 0; 25 | protected $width = 0; 26 | protected $red = 0; 27 | protected $green = 0; 28 | protected $blue = 0; 29 | protected $figureType = 'rectangle'; 30 | 31 | /** 32 | * Sets size for figure. 33 | * 34 | * @param int $width width 35 | * @param int $height height 36 | */ 37 | public function __construct($width = 0, $height = 0) 38 | { 39 | $this->setSize($width, $height); 40 | $this->toPNG(); 41 | 42 | parent::__construct(); 43 | } 44 | 45 | /** 46 | * Sets size to figure. 47 | * 48 | * @param int $width width 49 | * @param int $height height 50 | * 51 | * @return \GImage\Figure|static 52 | */ 53 | public function setSize($width = 0, $height = 0) 54 | { 55 | if (!empty($width) && !empty($height)) { 56 | $this->width = $this->boxWidth = $width; 57 | $this->height = $this->boxHeight = $height; 58 | } 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Sets the figure type as 'rectangle'. 65 | * 66 | * @return \GImage\Figure|static 67 | */ 68 | public function isRectangle() 69 | { 70 | $this->figureType = 'rectangle'; 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Sets the figure type as 'ellipse'. 77 | * 78 | * @return \GImage\Figure|static 79 | */ 80 | public function isEllipse() 81 | { 82 | $this->figureType = 'ellipse'; 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * Sets background color in RGB format. 89 | * 90 | * @param int $red red 91 | * @param int $green green 92 | * @param int $blue blue 93 | * 94 | * @return \GImage\Figure|static 95 | */ 96 | public function setBackgroundColor($red, $green, $blue) 97 | { 98 | $this->red = $red; 99 | $this->green = $green; 100 | $this->blue = $blue; 101 | 102 | return $this; 103 | } 104 | 105 | /** 106 | * Gets an array with the RGB background colors. 107 | * 108 | * @return array an array with RGB colors 109 | */ 110 | public function getBackgroundColor() 111 | { 112 | return [$this->red, $this->green, $this->blue]; 113 | } 114 | 115 | /** 116 | * Creates the figure with alpha channel. 117 | * 118 | * @return \GImage\Figure|static 119 | */ 120 | public function create() 121 | { 122 | $this->resource = imagecreatetruecolor($this->width, $this->height); 123 | imagesavealpha($this->resource, true); 124 | 125 | $color = imagecolorallocatealpha( 126 | $this->resource, 127 | $this->red, 128 | $this->green, 129 | $this->blue, 130 | 0 131 | ); 132 | 133 | if ($this->figureType == 'rectangle') { 134 | $this->createRectangle($color); 135 | } 136 | 137 | if ($this->figureType == 'ellipse') { 138 | $this->createEllipse($color); 139 | } 140 | 141 | $this->addOpacityFilter(); 142 | 143 | return $this; 144 | } 145 | 146 | /** 147 | * Creates a filled rectangle. 148 | * 149 | * @return void 150 | */ 151 | private function createRectangle($color) 152 | { 153 | imagefilledrectangle( 154 | $this->resource, 155 | 0, 156 | 0, 157 | $this->width, 158 | $this->height, 159 | $color 160 | ); 161 | } 162 | 163 | /** 164 | * Creates a filled ellipse. 165 | * 166 | * @return void 167 | */ 168 | private function createEllipse($color) 169 | { 170 | $alpha = imagecolorallocatealpha($this->resource, 255, 255, 255, 127); 171 | imagefill($this->resource, 0, 0, $alpha); 172 | 173 | imagefilledellipse( 174 | $this->resource, 175 | $this->width / 2, 176 | $this->height / 2, 177 | $this->width, 178 | $this->height, 179 | $color 180 | ); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/Image.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace GImage; 12 | 13 | /** 14 | * A simple extended GD class for easy image handling. 15 | * This is the parent class for Figure and Canvas. 16 | * 17 | * @author Jose Quintana 18 | * 19 | * @property string $name 20 | * @property string $filename 21 | * @property int $width 22 | * @property int $height 23 | * @property int $x 24 | * @property int $y 25 | * @property int $boxWidth 26 | * @property int $boxHeight 27 | * @property int $boxX 28 | * @property int $boxY 29 | * @property int $type IMAGETYPE_JPEG 30 | * @property string $extension Default 'jpg' 31 | * @property resource $resource|\GdImage 32 | * @property int $quality 33 | * @property int $opacity From 0 to 1 34 | * @property string $from Default 'local' 35 | * @property bool $preserve Default FALSE 36 | * @property string $mimetype Default 'image/jpeg' 37 | */ 38 | class Image 39 | { 40 | protected $name; 41 | protected $filename; 42 | protected $width = 0; 43 | protected $height = 0; 44 | protected $x = 0; 45 | protected $y = 0; 46 | protected $boxWidth = 0; 47 | protected $boxHeight = 0; 48 | protected $boxX = 0; 49 | protected $boxY = 0; 50 | protected $type = IMAGETYPE_JPEG; 51 | protected $extension = 'jpg'; 52 | protected $resource; 53 | protected $quality = 100; 54 | protected $opacity = 1; 55 | protected $from = 'local'; 56 | protected $preserve = false; 57 | protected $mimetype = 'image/jpeg'; 58 | 59 | /** 60 | * Loads an image from Image or Figure class. 61 | * 62 | * @param Image $element image or Figure class 63 | */ 64 | public function __construct($element = null) 65 | { 66 | $this->from($element); 67 | } 68 | 69 | /** 70 | * Loads an image from Image or Figure class. 71 | * 72 | * @param Image $element image or Figure class 73 | * 74 | * @return \GImage\Image|static 75 | */ 76 | public function from($element = null) 77 | { 78 | if (!empty($element) && $element instanceof Image) { 79 | foreach (get_object_vars($element) as $key => $value) { 80 | $this->$key = $value; 81 | } 82 | 83 | return $this; 84 | } 85 | } 86 | 87 | /** 88 | * Loads an image from a local path, external url or image string. 89 | * 90 | * @param string $src local path, external url or image string 91 | * 92 | * @return \GImage\Image|static 93 | */ 94 | public function load($src) 95 | { 96 | if (empty($src)) { 97 | return $this; 98 | } 99 | 100 | // 1. resource or \GdImage 101 | if (is_resource($src) || $src instanceof \GdImage) { 102 | $this->loadImageFromResource($src); 103 | // 2. string image 104 | } elseif (is_string($src) && $this->isImageStringByString($src)) { 105 | $this->loadImageFromString($src); 106 | // 3. string url image path 107 | } elseif (filter_var($src, FILTER_VALIDATE_URL)) { 108 | $this->loadImageFromURL($src); 109 | // 4. string file path 110 | } elseif (is_file($src)) { 111 | $this->loadImageFromFile($src); 112 | } 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * Load an image from URL. 119 | * 120 | * @param string $url Image URL 121 | * 122 | * @return void 123 | */ 124 | private function loadImageFromURL($url) 125 | { 126 | $image = $this->fetchImageContentFromURL($url); 127 | 128 | if (empty($image)) { 129 | return; 130 | } 131 | 132 | if (Utils::isJPGResource($image)) { 133 | $this->filename = $url; 134 | $this->extension = 'jpg'; 135 | $this->type = IMAGETYPE_JPEG; 136 | } 137 | 138 | if (Utils::isPNGResource($image)) { 139 | $this->filename = $url; 140 | $this->extension = 'png'; 141 | $this->type = IMAGETYPE_PNG; 142 | } 143 | 144 | $this->from = 'external'; 145 | $this->resource = imagecreatefromstring($image); 146 | $this->width = $this->boxWidth = imagesx($this->resource); 147 | $this->height = $this->boxHeight = imagesy($this->resource); 148 | } 149 | 150 | /** 151 | * Fetch an image string content from URL. 152 | * 153 | * @param string $url image URL 154 | * 155 | * @return string String data 156 | */ 157 | private function fetchImageContentFromURL($url) 158 | { 159 | $data = null; 160 | 161 | if ($stream = fopen($url, 'r')) { 162 | $data = stream_get_contents($stream); 163 | fclose($stream); 164 | } 165 | 166 | return $data; 167 | } 168 | 169 | /** 170 | * Load an image from a resource or `\GdImage`. 171 | * 172 | * @param string $resource image resource or `\GdImage` 173 | * 174 | * @return void 175 | */ 176 | private function loadImageFromResource($resource) 177 | { 178 | $this->extension = 'png'; 179 | $this->type = IMAGETYPE_PNG; 180 | $this->from = is_resource($resource) ? 'resource' : 'gdimage'; 181 | $this->resource = $resource; 182 | $this->width = $this->boxWidth = imagesx($this->resource); 183 | $this->height = $this->boxHeight = imagesy($this->resource); 184 | } 185 | 186 | /** 187 | * Load an image from image string. 188 | * 189 | * @param string $imagestring Image string 190 | * 191 | * @return void 192 | */ 193 | private function loadImageFromString($imagestring) 194 | { 195 | if (Utils::isJPGResource($imagestring)) { 196 | $this->extension = 'jpg'; 197 | $this->type = IMAGETYPE_JPEG; 198 | } 199 | 200 | if (Utils::isPNGResource($imagestring)) { 201 | $this->extension = 'png'; 202 | $this->type = IMAGETYPE_PNG; 203 | } 204 | 205 | $this->from = 'imagestring'; 206 | $this->resource = imagecreatefromstring($imagestring); 207 | $this->width = $this->boxWidth = imagesx($this->resource); 208 | $this->height = $this->boxHeight = imagesy($this->resource); 209 | } 210 | 211 | /** 212 | * Checks if string is Image string. 213 | * 214 | * @param string $imagestring image string 215 | * 216 | * @return bool 217 | */ 218 | private function isImageStringByString($imagestring) 219 | { 220 | return Utils::isJPGResource($imagestring) || Utils::isPNGResource($imagestring); 221 | } 222 | 223 | /** 224 | * Load an image file from path. 225 | * 226 | * @param string $filepath File path 227 | * 228 | * @return void 229 | */ 230 | private function loadImageFromFile($filepath) 231 | { 232 | $this->from = 'local'; 233 | $this->filename = $filepath; 234 | 235 | if (!is_readable($filepath)) { 236 | return; 237 | } 238 | 239 | $image = null; 240 | $extension = ''; 241 | list($width, $height, $imageType) = getimagesize($filepath); 242 | 243 | switch ($imageType) { 244 | case IMAGETYPE_GIF: 245 | $extension = 'gif'; 246 | $image = imagecreatefromgif($filepath); 247 | break; 248 | 249 | case IMAGETYPE_PNG: 250 | $extension = 'png'; 251 | $image = imagecreatefrompng($filepath); 252 | break; 253 | 254 | case IMAGETYPE_JPEG: 255 | $extension = 'jpg'; 256 | $image = imagecreatefromjpeg($filepath); 257 | break; 258 | } 259 | 260 | if ($image) { 261 | $this->type = $imageType; 262 | $this->resource = $image; 263 | $this->extension = $extension; 264 | $this->width = $this->boxWidth = $width; 265 | $this->height = $this->boxHeight = $height; 266 | } 267 | } 268 | 269 | /** 270 | * Gets the resource or `\GdImage` object (PHP 8.0+) of the image. 271 | * 272 | * @return resource|\GdImage 273 | */ 274 | public function getResource() 275 | { 276 | return $this->resource; 277 | } 278 | 279 | /** 280 | * Gets filename path of the image. 281 | * 282 | * @return string 283 | */ 284 | public function getFilename() 285 | { 286 | return $this->filename; 287 | } 288 | 289 | /** 290 | * Gets the image file name. 291 | * 292 | * @return string 293 | */ 294 | public function getName() 295 | { 296 | return $this->name; 297 | } 298 | 299 | /** 300 | * Gets the image type code (IMAGETYPE_JPEG, IMAGETYPE_PNG or IMAGETYPE_GIF). 301 | * 302 | * @return int 303 | */ 304 | public function getType() 305 | { 306 | return $this->type; 307 | } 308 | 309 | /** 310 | * Gets extension (jpg, png or gif). 311 | * 312 | * @return string 313 | */ 314 | public function getExtension() 315 | { 316 | return $this->extension; 317 | } 318 | 319 | /** 320 | * Gets quality. 321 | * 322 | * @return int 323 | */ 324 | public function getQuality() 325 | { 326 | return $this->quality; 327 | } 328 | 329 | /** 330 | * Gets opacity. 331 | * 332 | * @return int 333 | */ 334 | public function getOpacity() 335 | { 336 | return $this->opacity; 337 | } 338 | 339 | /** 340 | * Gets width. 341 | * 342 | * @return int 343 | */ 344 | public function getWidth() 345 | { 346 | return $this->width; 347 | } 348 | 349 | /** 350 | * Gets height. 351 | * 352 | * @return int 353 | */ 354 | public function getHeight() 355 | { 356 | return $this->height; 357 | } 358 | 359 | /** 360 | * Gets top position. 361 | * 362 | * @return int 363 | */ 364 | public function getTop() 365 | { 366 | return $this->y; 367 | } 368 | 369 | /** 370 | * Gets left position. 371 | * 372 | * @return int 373 | */ 374 | public function getLeft() 375 | { 376 | return $this->x; 377 | } 378 | 379 | /** 380 | * Gets box left position. 381 | * 382 | * @return int 383 | */ 384 | public function getBoxLeft() 385 | { 386 | return $this->boxX; 387 | } 388 | 389 | /** 390 | * Gets box top position. 391 | * 392 | * @return int 393 | */ 394 | public function getBoxTop() 395 | { 396 | return $this->boxY; 397 | } 398 | 399 | /** 400 | * Gets box width. 401 | * 402 | * @return int 403 | */ 404 | public function getBoxWidth() 405 | { 406 | return $this->boxWidth; 407 | } 408 | 409 | /** 410 | * Gets box height. 411 | * 412 | * @return int 413 | */ 414 | public function getBoxHeight() 415 | { 416 | return $this->boxHeight; 417 | } 418 | 419 | /** 420 | * Gets where the image was loaded: 'local' or 'external'. 421 | * 422 | * @return string 423 | */ 424 | public function getLoadedFrom() 425 | { 426 | return $this->from; 427 | } 428 | 429 | /** 430 | * Sets quality for image. 431 | * 432 | * @param int $quality Quality value from 0 to 100 433 | * 434 | * @return \GImage\Image|static 435 | */ 436 | public function setQuality($quality) 437 | { 438 | $this->quality = $quality; 439 | 440 | return $this; 441 | } 442 | 443 | /** 444 | * Sets opacity. 445 | * 446 | * @param int $opacity Opacity value from 0 to 127 447 | * 448 | * @return \GImage\Image|static 449 | */ 450 | public function setOpacity($opacity) 451 | { 452 | $this->opacity = $opacity > 127 ? 127 : $opacity; 453 | 454 | return $this; 455 | } 456 | 457 | /** 458 | * Sets image resource. 459 | * 460 | * @param resource $resource resource 461 | * 462 | * @return \GImage\Image|static 463 | */ 464 | public function setResource($resource) 465 | { 466 | $this->resource = $resource; 467 | 468 | return $this; 469 | } 470 | 471 | /** 472 | * Sets left position of image. 473 | * 474 | * @param int $x left 475 | * 476 | * @return \GImage\Image|static 477 | */ 478 | public function setLeft($x) 479 | { 480 | $this->x = $x; 481 | 482 | return $this; 483 | } 484 | 485 | /** 486 | * Sets top position of image. 487 | * 488 | * @param int $y top 489 | * 490 | * @return \GImage\Image|static 491 | */ 492 | public function setTop($y) 493 | { 494 | $this->y = $y; 495 | 496 | return $this; 497 | } 498 | 499 | /** 500 | * Sets width of box image. 501 | * 502 | * @param int $width width 503 | * 504 | * @return \GImage\Image|static 505 | */ 506 | public function setBoxWidth($width) 507 | { 508 | $this->boxWidth = $width; 509 | 510 | return $this; 511 | } 512 | 513 | /** 514 | * Sets height of box image. 515 | * 516 | * @param int $height height 517 | * 518 | * @return \GImage\Image|static 519 | */ 520 | public function setBoxHeight($height) 521 | { 522 | $this->boxHeight = $height; 523 | 524 | return $this; 525 | } 526 | 527 | /** 528 | * Sets left position of box image. 529 | * 530 | * @param int $x left position 531 | * 532 | * @return \GImage\Image|static 533 | */ 534 | public function setBoxLeft($x) 535 | { 536 | $this->boxX = $x * (-1); 537 | 538 | return $this; 539 | } 540 | 541 | /** 542 | * Sets top position of box image. 543 | * 544 | * @param int $y top position 545 | * 546 | * @return \GImage\Image|static 547 | */ 548 | public function setBoxTop($y) 549 | { 550 | $this->boxY = $y * (-1); 551 | 552 | return $this; 553 | } 554 | 555 | /** 556 | * Checks if image was loaded from local path. 557 | * 558 | * @return bool 559 | */ 560 | public function isLocal() 561 | { 562 | return $this->from == 'local'; 563 | } 564 | 565 | /** 566 | * Checks if image was loaded from external url. 567 | * 568 | * @return bool 569 | */ 570 | public function isExternal() 571 | { 572 | return $this->from == 'external'; 573 | } 574 | 575 | /** 576 | * Checks if image was loaded from image string. 577 | * 578 | * @return bool 579 | */ 580 | public function isImageString() 581 | { 582 | return $this->from == 'imagestring'; 583 | } 584 | 585 | /** 586 | * Checks if image was loaded from an image resource (PHP 7.4). 587 | * 588 | * @return bool 589 | */ 590 | public function isImageResource() 591 | { 592 | return $this->from == 'resource'; 593 | } 594 | 595 | /** 596 | * Checks if image was loaded from a `\GdImage` object (PHP 8.0+). 597 | * 598 | * @return bool 599 | */ 600 | public function isImageGdImage() 601 | { 602 | return $this->from == 'gdimage'; 603 | } 604 | 605 | /** 606 | * Checks if image is a JPG. 607 | * 608 | * @return bool 609 | */ 610 | public function isJPG() 611 | { 612 | return $this->extension == 'jpg'; 613 | } 614 | 615 | /** 616 | * Checks if image is a PNG. 617 | * 618 | * @return bool 619 | */ 620 | public function isPNG() 621 | { 622 | return $this->extension == 'png'; 623 | } 624 | 625 | /** 626 | * Checks if image is a GIF. 627 | * 628 | * @return bool 629 | */ 630 | public function isGIF() 631 | { 632 | return $this->extension == 'gif'; 633 | } 634 | 635 | /** 636 | * Changes output format to JPG. 637 | * 638 | * @return \GImage\Image|static 639 | */ 640 | public function toJPG() 641 | { 642 | $this->extension = 'jpg'; 643 | $this->type = IMAGETYPE_JPEG; 644 | $this->mimetype = Utils::getMimetypeByImageType(IMAGETYPE_JPEG); 645 | 646 | return $this; 647 | } 648 | 649 | /** 650 | * Changes output format to PNG. 651 | * 652 | * @return \GImage\Image|static 653 | */ 654 | public function toPNG() 655 | { 656 | $this->extension = 'png'; 657 | $this->type = IMAGETYPE_PNG; 658 | $this->mimetype = Utils::getMimetypeByImageType(IMAGETYPE_PNG); 659 | 660 | return $this; 661 | } 662 | 663 | /** 664 | * Changes output format to GIF. 665 | * 666 | * @return \GImage\Image|static 667 | */ 668 | public function toGIF() 669 | { 670 | $this->extension = 'gif'; 671 | $this->type = IMAGETYPE_GIF; 672 | $this->mimetype = Utils::getMimetypeByImageType(IMAGETYPE_GIF); 673 | 674 | return $this; 675 | } 676 | 677 | /** 678 | * Preserves the resource image when save or output function is called. 679 | * 680 | * @param bool $preserve if it's true will preserve the resource image 681 | * 682 | * @return \GImage\Image|static 683 | */ 684 | public function preserve($preserve = true) 685 | { 686 | $this->preserve = $preserve; 687 | 688 | return $this; 689 | } 690 | 691 | /** 692 | * Resize image proportionally basing on the height of the image. 693 | * 694 | * @param int $height 695 | * 696 | * @return \GImage\Image|static 697 | */ 698 | public function resizeToHeight($height) 699 | { 700 | $width = $this->getPropWidth($height); 701 | $this->resize($width, $height); 702 | 703 | return $this; 704 | } 705 | 706 | /** 707 | * Resize image proportionally basing on the width of the image. 708 | * 709 | * @param int $width 710 | * 711 | * @return \GImage\Image|static 712 | */ 713 | public function resizeToWidth($width) 714 | { 715 | $height = $this->getPropHeight($width); 716 | $this->resize($width, $height); 717 | 718 | return $this; 719 | } 720 | 721 | /** 722 | * Gets proportional width of image from height value. 723 | * 724 | * @param int $height 725 | * 726 | * @return int 727 | */ 728 | public function getPropWidth($height) 729 | { 730 | $ratio = $height / $this->height; 731 | 732 | return (int) floor($this->width * $ratio); 733 | } 734 | 735 | /** 736 | * Gets proportional height of image from width value. 737 | * 738 | * @param int $width 739 | * 740 | * @return int 741 | */ 742 | public function getPropHeight($width) 743 | { 744 | $ratio = $width / $this->width; 745 | 746 | return (int) floor($this->height * $ratio); 747 | } 748 | 749 | /** 750 | * Scales the image. 751 | * 752 | * @param int|float $scale 753 | * 754 | * @return \GImage\Image|static 755 | */ 756 | public function scale($scale = 1) 757 | { 758 | $scale = $scale > 1 ? 1 : $scale; 759 | $scale = $scale < 0 ? 0 : $scale; 760 | 761 | $width = (int) floor($this->width * $scale); 762 | $height = (int) floor($this->height * $scale); 763 | 764 | $this->resize($width, $height); 765 | 766 | return $this; 767 | } 768 | 769 | /** 770 | * Rotate the image with a given angle. 771 | * 772 | * @param int $angle 773 | * 774 | * @return \GImage\Image|static 775 | */ 776 | public function rotate($angle = 0) 777 | { 778 | if ($this->resource) { 779 | $this->resource = imagerotate($this->resource, $angle, 0); 780 | } 781 | 782 | return $this; 783 | } 784 | 785 | /** 786 | * Cuts the image proportionally and centered. 787 | * 788 | * @param int $width width crop 789 | * @param int $height height crop 790 | * 791 | * @return \GImage\Image|static 792 | */ 793 | public function centerCrop($width, $height) 794 | { 795 | $pwidth = $this->getPropWidth($height); 796 | $pheight = $this->getPropHeight($width); 797 | 798 | if ($pwidth == $width && $pheight == $height) { 799 | $this->resizeToWidth($width); 800 | } else { 801 | if ($pheight > $height) { 802 | $this->resizeToWidth($width); 803 | } else { 804 | $pheight += $height - $pheight; 805 | $this->resizeToHeight($pheight); 806 | } 807 | 808 | $x = (int) floor(($this->width - $width) / 2); 809 | $y = (int) floor(($this->height - $height) / 2); 810 | $this->crop($width, $height, $x, $y); 811 | } 812 | 813 | return $this; 814 | } 815 | 816 | /** 817 | * Cuts part of image. 818 | * 819 | * @param int $width width crop 820 | * @param int $height height crop 821 | * @param int $x1 [Optional] x-coordinate of source point 822 | * @param int $y1 [Optional] y-coordinate of source point 823 | * @param int $dstX [Optional] x-coordinate of destination point 824 | * @param int $dstY [Optional] y-coordinate of destination point 825 | * 826 | * @return \GImage\Image|static 827 | */ 828 | public function crop($width, $height, $x1 = 0, $y1 = 0, $dstX = 0, $dstY = 0) 829 | { 830 | $this->resize($width, $height, $x1, $y1, $dstX, $dstY, true); 831 | 832 | return $this; 833 | } 834 | 835 | /** 836 | * Resizes the image. 837 | * 838 | * @param int $width image's width 839 | * @param int $height image's height 840 | * @param int $x1 [Optional] Left position 841 | * @param int $y1 [Optional] Top position 842 | * @param int $dstX [Optional] x-coordinate of destination point 843 | * @param int $dstY [Optional] y-coordinate of destination point 844 | * @param bool $isCrop [Optional] if it's true resize function will crop the image 845 | * 846 | * @return \GImage\Image|static 847 | */ 848 | private function resize($width, $height, $x1 = 0, $y1 = 0, $dstX = 0, $dstY = 0, $isCrop = false) 849 | { 850 | if ($this->resource && $width > 0 && $height > 0) { 851 | $image = imagecreatetruecolor($width, $height); 852 | 853 | if ($this->isPNG()) { 854 | imagealphablending($image, false); 855 | imagesavealpha($image, true); 856 | } 857 | 858 | imagecopyresampled( 859 | $image, 860 | $this->resource, 861 | $dstX, 862 | $dstY, 863 | $x1, 864 | $y1, 865 | $width, 866 | $height, 867 | $isCrop ? $width : $this->width, 868 | $isCrop ? $height : $this->height 869 | ); 870 | 871 | $this->resource = $image; 872 | $this->width = $this->boxWidth = imagesx($this->resource); 873 | $this->height = $this->boxHeight = imagesy($this->resource); 874 | } 875 | 876 | return $this; 877 | } 878 | 879 | /** 880 | * Saves the image to specific path. 881 | * 882 | * @param string $filename if it's null save function will save the image 883 | * in load path for default 884 | * 885 | * @return \GImage\Image|static 886 | */ 887 | public function save($filename = null) 888 | { 889 | return $this->outputBuffer($filename); 890 | } 891 | 892 | /** 893 | * Outputs the image on browser. 894 | * 895 | * @return \GImage\Image|static 896 | */ 897 | public function output() 898 | { 899 | return $this->outputBuffer(null, true); 900 | } 901 | 902 | /** 903 | * Render the image in-memory and return the resource. 904 | * 905 | * @return resource | null Return the resource or null 906 | */ 907 | public function render() 908 | { 909 | $image = null; 910 | 911 | if ($this->resource) { 912 | ob_start(); 913 | $this->outputBufferByImage(null, $this->quality); 914 | $string = ob_get_contents(); 915 | ob_end_clean(); 916 | 917 | if (!empty($string)) { 918 | $image = imagecreatefromstring($string); 919 | } 920 | } 921 | 922 | return $image; 923 | } 924 | 925 | /** 926 | * Output the image to either the browser or a file. 927 | * 928 | * @param string $filename [Optional] Path to save image 929 | * @param bool $output [Optional] true to output the image 930 | * 931 | * @return \GImage\Image|static | resource 932 | */ 933 | private function outputBuffer($filename = null, $output = false) 934 | { 935 | if (!$this->resource) { 936 | return $this; 937 | } 938 | 939 | if ($output) { 940 | $filename = null; 941 | } elseif (empty($filename) && $this->isLocal()) { 942 | $filename = $this->filename; 943 | } elseif (empty($filename) && $this->isExternal()) { 944 | $filename = $this->name; 945 | } 946 | 947 | if ($output) { 948 | header('Content-type: ' . $this->mimetype); 949 | ob_start(); 950 | } 951 | 952 | $this->outputBufferByImage($filename, $this->quality); 953 | 954 | if ($output) { 955 | ob_end_flush(); 956 | } 957 | 958 | if (!$this->preserve) { 959 | $this->destroy(); 960 | } 961 | 962 | return $this; 963 | } 964 | 965 | private function outputBufferByImage($filename, $quality) 966 | { 967 | if ($this->isJPG()) { 968 | imagejpeg($this->resource, $filename, $quality); 969 | } 970 | 971 | if ($this->isPNG()) { 972 | if ($quality > 10) { 973 | $quality = 0; 974 | } 975 | 976 | imagesavealpha($this->resource, true); 977 | 978 | if (!is_subclass_of($this, Image::class)) { 979 | $this->addOpacityFilter(); 980 | } 981 | 982 | imagepng($this->resource, $filename, $quality); 983 | } 984 | 985 | if ($this->isGIF()) { 986 | imagegif($this->resource, $filename); 987 | } 988 | } 989 | 990 | /** 991 | * Destroys the current resource. 992 | * 993 | * @return \GImage\Image|static 994 | */ 995 | public function destroy() 996 | { 997 | if ($this->resource) { 998 | imagedestroy($this->resource); 999 | } 1000 | 1001 | $this->resource = null; 1002 | 1003 | return $this; 1004 | } 1005 | 1006 | /** 1007 | * Add opacity filter to the current resource. 1008 | * 1009 | * @return void 1010 | */ 1011 | protected function addOpacityFilter() 1012 | { 1013 | $opacity = Utils::fixPNGOpacity($this->opacity); 1014 | 1015 | if ($opacity >= 0 && $opacity < 127) { 1016 | imagealphablending($this->resource, false); 1017 | imagefilter($this->resource, IMG_FILTER_COLORIZE, 0, 0, 0, $opacity); 1018 | } 1019 | } 1020 | } 1021 | -------------------------------------------------------------------------------- /src/Text.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace GImage; 12 | 13 | /** 14 | * A Text class to embed string into Canvas. 15 | * 16 | * @author Jose Quintana 17 | * 18 | * @property string $string String text. 19 | * @property string $fontface Font face .ttf filename. 20 | * @property string $align Horizontal align mode. 21 | * @property string $valign Vertical align mode. 22 | * @property int $angle Angle for the text. 23 | * @property int $opacity Opacity for the text. 24 | * @property int $width 25 | * @property int $height 26 | * @property float $lineHeight 27 | * @property int $size 28 | * @property int $x 29 | * @property int $y 30 | * @property int $red 31 | * @property int $green 32 | * @property int $blue 33 | */ 34 | class Text 35 | { 36 | private $string = ''; 37 | private $fontface; 38 | private $align = 'none'; 39 | private $valign = 'none'; 40 | private $angle = 0; 41 | private $opacity = 0; 42 | private $width = 100; 43 | private $height = 50; 44 | private $lineHeight = 1.2; 45 | private $size = 12; 46 | private $x = 0; 47 | private $y = 0; 48 | private $red = 0; 49 | private $green = 0; 50 | private $blue = 0; 51 | 52 | /** 53 | * Sets the plain text. 54 | * 55 | * @param string $string plain text 56 | */ 57 | public function __construct($string = '') 58 | { 59 | $this->setContent($string); 60 | } 61 | 62 | /** 63 | * Sets a plain text. 64 | * 65 | * @param string $string plain text 66 | * 67 | * @return $this 68 | */ 69 | public function setContent($string = '') 70 | { 71 | $this->string = $string; 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * Sets RGB color for text. 78 | * 79 | * @param int $red red color 80 | * @param int $green green color 81 | * @param int $blue blue color 82 | * 83 | * @return $this 84 | */ 85 | public function setColor($red, $green, $blue) 86 | { 87 | $this->red = $red; 88 | $this->green = $green; 89 | $this->blue = $blue; 90 | 91 | return $this; 92 | } 93 | 94 | /** 95 | * Sets font size for text. 96 | * 97 | * @param int $size font size 98 | * 99 | * @return $this 100 | */ 101 | public function setSize($size) 102 | { 103 | $this->size = $size; 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Sets font face (TTF font) for text. 110 | * 111 | * @param string $fontface path of TTF font 112 | * 113 | * @return $this 114 | */ 115 | public function setFontface($fontface) 116 | { 117 | $this->fontface = $fontface; 118 | 119 | return $this; 120 | } 121 | 122 | /** 123 | * Sets text's opacity. 124 | * 125 | * @param int $opacity Opacity value from 0 to 1 126 | * 127 | * @return $this 128 | */ 129 | public function setOpacity($opacity) 130 | { 131 | $this->opacity = $opacity; 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * Sets the horizontal alignment for text. 138 | * 139 | * @param string $align Values supported: none, center 140 | * 141 | * @return $this 142 | */ 143 | public function setAlign($align) 144 | { 145 | $this->align = $align; 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | * Sets the vertical alignment for text. 152 | * 153 | * @param string $valign Two values supported: 'none' or 'center' 154 | * 155 | * @return $this 156 | */ 157 | public function setValign($valign) 158 | { 159 | $this->valign = $valign; 160 | 161 | return $this; 162 | } 163 | 164 | /** 165 | * Sets text's angle. 166 | * 167 | * @param int $angle Angle 168 | * 169 | * @return $this 170 | */ 171 | public function setAngle($angle) 172 | { 173 | $this->angle = $angle; 174 | 175 | return $this; 176 | } 177 | 178 | /** 179 | * Sets box width. 180 | * 181 | * @param int $width Width 182 | * 183 | * @return $this 184 | */ 185 | public function setWidth($width) 186 | { 187 | $this->width = $width; 188 | 189 | return $this; 190 | } 191 | 192 | /** 193 | * Sets box height. 194 | * 195 | * @param int $height Height 196 | * 197 | * @return $this 198 | */ 199 | public function setHeight($height) 200 | { 201 | $this->height = $height; 202 | 203 | return $this; 204 | } 205 | 206 | /** 207 | * Sets top position. 208 | * 209 | * @param int $y position 210 | * 211 | * @return $this 212 | */ 213 | public function setTop($y) 214 | { 215 | $this->y = $y; 216 | 217 | return $this; 218 | } 219 | 220 | /** 221 | * Sets left position. 222 | * 223 | * @param int $x position 224 | * 225 | * @return $this 226 | */ 227 | public function setLeft($x) 228 | { 229 | $this->x = $x; 230 | 231 | return $this; 232 | } 233 | 234 | /** 235 | * Sets line height. 236 | * 237 | * @param float $lineHeight line-height 238 | * 239 | * @return $this 240 | */ 241 | public function setLineHeight($lineHeight) 242 | { 243 | $this->lineHeight = $lineHeight; 244 | 245 | return $this; 246 | } 247 | 248 | /** 249 | * Gets line height. 250 | * 251 | * @return float 252 | */ 253 | public function getLineHeight() 254 | { 255 | return $this->lineHeight; 256 | } 257 | 258 | /** 259 | * Wrapps the text. 260 | * 261 | * @return array 262 | */ 263 | public function wrappText() 264 | { 265 | $wrapp = $this->getWrappedText( 266 | $this->size, 267 | $this->angle, 268 | $this->fontface, 269 | $this->string, 270 | $this->width 271 | ); 272 | 273 | return $wrapp; 274 | } 275 | 276 | /** 277 | * Gets wrapped text. 278 | * 279 | * @param int $size font size fot the text 280 | * @param int $angle angole for the text 281 | * @param string $fontface path of TTF font 282 | * @param string $string string text 283 | * @param int $width width for text box area 284 | * 285 | * @return array 286 | */ 287 | public function getWrappedText($size, $angle, $fontface, $string, $width = 100) 288 | { 289 | $str = ''; 290 | $words = explode(' ', $string); 291 | 292 | foreach ($words as $word) { 293 | $testStr = $str . ' ' . $word; 294 | $box = imagettfbbox($size, $angle, $fontface, $testStr); 295 | 296 | if ($box[2] > $width) { 297 | $str .= ($str == '' ? '' : "\n") . $word; 298 | } else { 299 | $str .= ($str == '' ? '' : ' ') . $word; 300 | } 301 | } 302 | 303 | $this->string = $str; 304 | $lines = explode("\n", $str); 305 | 306 | return $lines; 307 | } 308 | 309 | /** 310 | * Calculates bounding box of text using the TrueType font. 311 | * Returns an array with 'left', 'top', 'width' and 'height' values. 312 | * 313 | * @author 314 | * 315 | * @param int $fontSize font size fot the text 316 | * @param int $fontAngle angole for the text 317 | * @param string $fontFile path of TTF font 318 | * @param string $text string text 319 | * 320 | * @return array|bool 321 | */ 322 | private function getBoundingBox($fontSize, $fontAngle, $fontFile, $text) 323 | { 324 | $box = imagettfbbox($fontSize, $fontAngle, $fontFile, $text); 325 | 326 | if (!$box) { 327 | return false; 328 | } 329 | 330 | $minX = min([$box[0], $box[2], $box[4], $box[6]]); 331 | $maxX = max([$box[0], $box[2], $box[4], $box[6]]); 332 | $minY = min([$box[1], $box[3], $box[5], $box[7]]); 333 | $maxY = max([$box[1], $box[3], $box[5], $box[7]]); 334 | $width = ($maxX - $minX); 335 | $height = ($maxY - $minY); 336 | $left = abs($minX) + $width; 337 | $top = abs($minY) + $height; 338 | 339 | // to calculate the exact bounding box i write the text in a large image 340 | $img = imagecreatetruecolor($width << 2, $height << 2); 341 | $white = imagecolorallocate($img, 255, 255, 255); 342 | $black = imagecolorallocate($img, 0, 0, 0); 343 | imagefilledrectangle($img, 0, 0, imagesx($img), imagesy($img), $black); 344 | // for sure the text is completely in the image! 345 | imagettftext($img, $fontSize, $fontAngle, $left, $top, $white, $fontFile, $text); 346 | // start scanning (0=> black => empty) 347 | $rleft = $w4 = $width << 2; 348 | $rright = 0; 349 | $rbottom = 0; 350 | $rtop = $h4 = $height << 2; 351 | 352 | for ($x = 0; $x < $w4; $x++) { 353 | for ($y = 0; $y < $h4; $y++) { 354 | if (imagecolorat($img, $x, $y)) { 355 | $rleft = min($rleft, $x); 356 | $rright = max($rright, $x); 357 | $rtop = min($rtop, $y); 358 | $rbottom = max($rbottom, $y); 359 | } 360 | } 361 | } 362 | 363 | // destroy img and serve the result 364 | imagedestroy($img); 365 | 366 | return [ 367 | 'left' => $left - $rleft, 368 | 'top' => $top - $rtop, 369 | 'width' => $rright - $rleft + 1, 370 | 'height' => $rbottom - $rtop + 1, 371 | ]; 372 | } 373 | 374 | /** 375 | * Get the text cords [x, y]. 376 | * 377 | * @return array an array with [x, y] cords 378 | */ 379 | public function getCords() 380 | { 381 | $box = $this->getBoundingBox( 382 | $this->getSize(), 383 | $this->getAngle(), 384 | $this->getFontface(), 385 | $this->getContent() 386 | ); 387 | 388 | $x = $box['left'] + $this->getLeft(); 389 | $y = $this->getTop() + $box['top']; 390 | 391 | if ($this->getAlign() == 'center') { 392 | $x = ($this->getWidth() - $box['width']) / 2; 393 | } 394 | 395 | if ($this->getValign() == 'center') { 396 | $y = ($this->getHeight() - $box['height']) / 2; 397 | } 398 | 399 | return [$x, $y]; 400 | } 401 | 402 | /** 403 | * Gets the plain text. 404 | * 405 | * @return string 406 | */ 407 | public function getContent() 408 | { 409 | return $this->string; 410 | } 411 | 412 | /** 413 | * Gets an array with rgb color. 414 | * 415 | * @return array 416 | */ 417 | public function getColor() 418 | { 419 | return [ 420 | $this->red, 421 | $this->green, 422 | $this->blue, 423 | ]; 424 | } 425 | 426 | /** 427 | * Gets the font face. 428 | * 429 | * @return string 430 | */ 431 | public function getFontface() 432 | { 433 | return $this->fontface; 434 | } 435 | 436 | /** 437 | * Gets fthe ont size. 438 | * 439 | * @return int 440 | */ 441 | public function getSize() 442 | { 443 | return $this->size; 444 | } 445 | 446 | /** 447 | * Gets the opacity. 448 | * 449 | * @return int 450 | */ 451 | public function getOpacity() 452 | { 453 | return $this->opacity; 454 | } 455 | 456 | /** 457 | * Gets the horizontal alignment for text. 458 | * 459 | * @return string alignment supported: 'none' or 'center' 460 | */ 461 | public function getAlign() 462 | { 463 | return $this->align; 464 | } 465 | 466 | /** 467 | * Gets the vertical alignment for text. 468 | * 469 | * @return string alignment supported: 'none' or 'center' 470 | */ 471 | public function getValign() 472 | { 473 | return $this->valign; 474 | } 475 | 476 | /** 477 | * Gets the angle. 478 | * 479 | * @return int 480 | */ 481 | public function getAngle() 482 | { 483 | return $this->angle; 484 | } 485 | 486 | /** 487 | * Gets width. 488 | * 489 | * @return int 490 | */ 491 | public function getWidth() 492 | { 493 | return $this->width; 494 | } 495 | 496 | /** 497 | * Gets height. 498 | * 499 | * @return int 500 | */ 501 | public function getHeight() 502 | { 503 | return $this->height; 504 | } 505 | 506 | /** 507 | * Gets top position. 508 | * 509 | * @return int 510 | */ 511 | public function getTop() 512 | { 513 | return $this->y; 514 | } 515 | 516 | /** 517 | * Gets left position. 518 | * 519 | * @return int 520 | */ 521 | public function getLeft() 522 | { 523 | return $this->x; 524 | } 525 | } 526 | -------------------------------------------------------------------------------- /src/Utils.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This source file is subject to the MIT license that is bundled 8 | * with this source code in the file LICENSE. 9 | */ 10 | 11 | namespace GImage; 12 | 13 | /** 14 | * Image utility functions. 15 | * 16 | * @author Jose Quintana 17 | * 18 | * @property array $mimeTypes Mime types for images. 19 | * @property array $imageTypes Types: IMAGETYPE_GIF, IMAGETYPE_PNG and IMAGETYPE_JPEG. 20 | */ 21 | class Utils 22 | { 23 | private static $mimeTypes = [ 24 | IMAGETYPE_GIF => 'image/gif', 25 | IMAGETYPE_PNG => 'image/png', 26 | IMAGETYPE_JPEG => 'image/jpeg', 27 | ]; 28 | private static $imageTypes = [ 29 | 'gif' => IMAGETYPE_GIF, 30 | 'png' => IMAGETYPE_PNG, 31 | 'jpg' => IMAGETYPE_JPEG, 32 | ]; 33 | 34 | /** 35 | * Gets image mime types (jpg, png and gif). 36 | * 37 | * @return array 38 | */ 39 | public static function getMimetypes() 40 | { 41 | return self::$mimeTypes; 42 | } 43 | 44 | /** 45 | * Gets image mimeType by filename. 46 | * 47 | * @param string $filename image path 48 | * 49 | * @return string 50 | */ 51 | public static function getMimetype($filename) 52 | { 53 | return self::$mimeTypes[self::getImageType($filename)]; 54 | } 55 | 56 | /** 57 | * Gets image mime type by image type (IMAGETYPE_GIF, IMAGETYPE_PNG or IMAGETYPE_JPEG). 58 | * 59 | * @param string $imagetype IMAGETYPE_GIF, IMAGETYPE_PNG or IMAGETYPE_JPEG 60 | * 61 | * @return string 62 | */ 63 | public static function getMimetypeByImageType($imagetype) 64 | { 65 | return self::$mimeTypes[$imagetype]; 66 | } 67 | 68 | /** 69 | * Gets image extension from filename. 70 | * 71 | * @param string $filename image path 72 | * 73 | * @return string return jpg, png or gif extension 74 | */ 75 | public static function getExtension($filename) 76 | { 77 | return strtolower(preg_replace('/^(.+)\./', '', $filename)); 78 | } 79 | 80 | /** 81 | * Gets image type from filename. 82 | * 83 | * @param string $filename image path 84 | * 85 | * @return bool 86 | */ 87 | public static function getImageType($filename) 88 | { 89 | return self::$imageTypes[self::getExtension($filename)]; 90 | } 91 | 92 | /** 93 | * Checks if image path is a JPG. 94 | * 95 | * @param string $filename image path 96 | * 97 | * @return bool 98 | */ 99 | public static function isJPG($filename) 100 | { 101 | return self::getExtension($filename) == 'jpg'; 102 | } 103 | 104 | /** 105 | * Checks if image path is a PNG. 106 | * 107 | * @param string $filename image path 108 | * 109 | * @return bool 110 | */ 111 | public static function isPNG($filename) 112 | { 113 | return self::getExtension($filename) == 'png'; 114 | } 115 | 116 | /** 117 | * Checks if image path is a PNG. 118 | * 119 | * @param string $filename image path 120 | * 121 | * @return bool 122 | */ 123 | public static function isGIF($filename) 124 | { 125 | return self::getExtension($filename) == 'gif'; 126 | } 127 | 128 | /** 129 | * Checks if image resource is a JPG. 130 | * 131 | * @param string $resource image resource 132 | * 133 | * @return bool 134 | */ 135 | public static function isJPGResource($resource) 136 | { 137 | return $resource && (bin2hex($resource[0]) == 'ff' 138 | && bin2hex($resource[1]) == 'd8'); 139 | } 140 | 141 | /** 142 | * Checks if image resource is a PNG. 143 | * 144 | * @param string $resource image resource 145 | * 146 | * @return bool 147 | */ 148 | public static function isPNGResource($resource) 149 | { 150 | return $resource && (bin2hex($resource[0]) == '89' && $resource[1] == 'P' 151 | && $resource[2] == 'N' && $resource[3] == 'G'); 152 | } 153 | 154 | /** 155 | * Fix the opacity value (0 to 1) for PNG alpha value. 156 | * 157 | * @param int|float $opacity 158 | * 159 | * @return int 160 | */ 161 | public static function fixPNGOpacity($opacity = 1) 162 | { 163 | $opacity = $opacity > 1 ? 1 : $opacity; 164 | $opacity = $opacity < 0 ? 0 : $opacity; 165 | $opacity = (int) round(127 * $opacity, 0, PHP_ROUND_HALF_UP); 166 | $opacity = 127 - $opacity; 167 | 168 | return $opacity; 169 | } 170 | } 171 | --------------------------------------------------------------------------------