Issues (61)

src/Helpers/ImageHelper.php (13 issues)

1
<?php
2
3
namespace Itstructure\MFU\Helpers;
4
5
use Exception;
6
use Illuminate\Support\Arr;
7
use Imagine\Filter\Basic\Autorotate;
0 ignored issues
show
The type Imagine\Filter\Basic\Autorotate was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Imagine\Image\{Box, BoxInterface, ImageInterface, ImagineInterface, ManipulatorInterface, Palette\RGB, Point};
0 ignored issues
show
The type Imagine\Image\ManipulatorInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
The type Imagine\Image\Palette\RGB was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
The type Imagine\Image\BoxInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
The type Imagine\Image\ImageInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
The type Imagine\Image\Point was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
The type Imagine\Image\Box was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
The type Imagine\Image\ImagineInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
10
class ImageHelper
11
{
12
    /**
13
     * GD2 driver definition for Imagine implementation using the GD library.
14
     */
15
    const DRIVER_GD2 = 'gd2';
16
    /**
17
     * imagick driver definition.
18
     */
19
    const DRIVER_IMAGICK = 'imagick';
20
    /**
21
     * gmagick driver definition.
22
     */
23
    const DRIVER_GMAGICK = 'gmagick';
24
25
    /**
26
     * @var array|string the driver to use. This can be either a single driver name or an array of driver names.
27
     * If the latter, the first available driver will be used.
28
     */
29
    public static $driver = [self::DRIVER_GMAGICK, self::DRIVER_IMAGICK, self::DRIVER_GD2];
30
    /**
31
     * @var ImagineInterface instance.
32
     */
33
    private static $_imagine;
34
35
36
    /**
37
     * @var string background color to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET` mode with
38
     * both width and height specified. Default is white.
39
     */
40
    public static $thumbnailBackgroundColor = 'FFF';
41
    /**
42
     * @var string background alpha (transparency) to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET`
43
     * mode with both width and height specified. Default is solid.
44
     */
45
    public static $thumbnailBackgroundAlpha = 100;
46
47
    /**
48
     * Returns the `Imagine` object that supports various image manipulations.
49
     * @return ImagineInterface the `Imagine` object
50
     */
51
    public static function getImagine()
52
    {
53
        if (self::$_imagine === null) {
54
            self::$_imagine = static::createImagine();
55
        }
56
57
        return self::$_imagine;
58
    }
59
60
    /**
61
     * @param ImagineInterface $imagine the `Imagine` object.
62
     */
63
    public static function setImagine($imagine)
64
    {
65
        self::$_imagine = $imagine;
66
    }
67
68
    /**
69
     * Creates an `Imagine` object based on the specified [[driver]].
70
     * @return ImagineInterface the new `Imagine` object
71
     * @throws Exception if [[driver]] is unknown or the system doesn't support any [[driver]].
72
     */
73
    protected static function createImagine()
74
    {
75
        foreach ((array)static::$driver as $driver) {
76
            switch ($driver) {
77
                case self::DRIVER_GMAGICK:
78
                    if (class_exists('Gmagick', false)) {
79
                        return new \Imagine\Gmagick\Imagine();
0 ignored issues
show
The type Imagine\Gmagick\Imagine was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
80
                    }
81
                    break;
82
                case self::DRIVER_IMAGICK:
83
                    if (class_exists('Imagick', false)) {
84
                        return new \Imagine\Imagick\Imagine();
0 ignored issues
show
The type Imagine\Imagick\Imagine was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
85
                    }
86
                    break;
87
                case self::DRIVER_GD2:
88
                    if (function_exists('gd_info')) {
89
                        return new \Imagine\Gd\Imagine();
0 ignored issues
show
The type Imagine\Gd\Imagine was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
90
                    }
91
                    break;
92
                default:
93
                    throw new Exception("Unknown driver: $driver");
94
            }
95
        }
96
        throw new Exception('Your system does not support any of these drivers: ' . implode(',', (array)static::$driver));
97
    }
98
99
    /**
100
     * Takes either file path or ImageInterface. In case of file path, creates an instance of ImageInterface from it.
101
     *
102
     * @param string|resource|ImageInterface $image
103
     * @return ImageInterface
104
     * @throws Exception
105
     */
106
    protected static function ensureImageInterfaceInstance($image)
107
    {
108
        if ($image instanceof ImageInterface) {
109
            return $image;
110
        }
111
112
        if (is_resource($image)) {
113
            return static::getImagine()->read($image);
114
        }
115
116
        if (is_string($image)) {
0 ignored issues
show
The condition is_string($image) is always true.
Loading history...
117
            return static::getImagine()->open($image);
118
        }
119
120
        throw new Exception('File should be either ImageInterface, resource or a string containing file path.');
121
    }
122
123
    /**
124
     * Crops an image.
125
     *
126
     * For example:
127
     *
128
     * ```php
129
     * $obj->crop('path\to\image.jpg', 200, 200, [5, 5]);
130
     *
131
     * $point = new \Imagine\Image\Point(5, 5);
132
     * $obj->crop('path\to\image.jpg', 200, 200, $point);
133
     * ```
134
     *
135
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
136
     * @param int $width the crop width
137
     * @param int $height the crop height
138
     * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
139
     * @return ImageInterface
140
     * @throws Exception if the `$start` parameter is invalid
141
     */
142
    public static function crop($image, $width, $height, array $start = [0, 0])
143
    {
144
        if (!isset($start[0], $start[1])) {
145
            throw new Exception('$start must be an array of two elements.');
146
        }
147
148
        return static::ensureImageInterfaceInstance($image)
149
            ->copy()
150
            ->crop(new Point($start[0], $start[1]), new Box($width, $height));
151
    }
152
153
    /**
154
     * Rotates an image automatically based on EXIF information.
155
     *
156
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
157
     * @param string $color
158
     * @return \Imagine\Image\ImageInterface
159
     */
160
    public static function autorotate($image, $color = '000000')
161
    {
162
        return (new Autorotate($color))->apply(static::ensureImageInterfaceInstance($image));
163
    }
164
165
    /**
166
     * Creates a thumbnail image.
167
     *
168
     * If one of thumbnail dimensions is set to `null`, another one is calculated automatically based on aspect ratio of
169
     * original image. Note that calculated thumbnail dimension may vary depending on the source image in this case.
170
     *
171
     * If both dimensions are specified, resulting thumbnail would be exactly the width and height specified. How it's
172
     * achieved depends on the mode.
173
     *
174
     * If `ImageInterface::THUMBNAIL_OUTBOUND` mode is used, which is default, then the thumbnail is scaled so that
175
     * its smallest side equals the length of the corresponding side in the original image. Any excess outside of
176
     * the scaled thumbnail’s area will be cropped, and the returned thumbnail will have the exact width and height
177
     * specified.
178
     *
179
     * If thumbnail mode is `ImageInterface::THUMBNAIL_INSET`, the original image is scaled down so it is fully
180
     * contained within the thumbnail dimensions. The rest is filled with background that could be configured via
181
     * [[Image::$thumbnailBackgroundColor]] and [[Image::$thumbnailBackgroundAlpha]].
182
     *
183
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
184
     * @param int $width the width in pixels to create the thumbnail
185
     * @param int $height the height in pixels to create the thumbnail
186
     * @param string $mode mode of resizing original image to use in case both width and height specified
187
     * @return ImageInterface
188
     */
189
    public static function thumbnail($image, $width, $height, $mode = ManipulatorInterface::THUMBNAIL_OUTBOUND)
190
    {
191
        $img = self::ensureImageInterfaceInstance($image);
192
193
        /** @var BoxInterface $sourceBox */
194
        $sourceBox = $img->getSize();
195
        $thumbnailBox = static::getThumbnailBox($sourceBox, $width, $height);
196
197
        if (self::isUpscaling($sourceBox, $thumbnailBox)) {
198
            return $img->copy();
199
        }
200
201
        $img = $img->thumbnail($thumbnailBox, $mode);
202
203
        if ($mode == ManipulatorInterface::THUMBNAIL_OUTBOUND) {
204
            return $img;
205
        }
206
207
        $size = $img->getSize();
208
209
        if ($size->getWidth() == $width && $size->getHeight() == $height) {
210
            return $img;
211
        }
212
213
        $palette = new RGB();
214
        $color = $palette->color(static::$thumbnailBackgroundColor, static::$thumbnailBackgroundAlpha);
215
216
        // create empty image to preserve aspect ratio of thumbnail
217
        $thumb = static::getImagine()->create($thumbnailBox, $color);
218
219
        // calculate points
220
        $startX = 0;
221
        $startY = 0;
222
        if ($size->getWidth() < $width) {
223
            $startX = ceil(($width - $size->getWidth()) / 2);
224
        }
225
        if ($size->getHeight() < $height) {
226
            $startY = ceil(($height - $size->getHeight()) / 2);
227
        }
228
229
        $thumb->paste($img, new Point($startX, $startY));
230
231
        return $thumb;
232
    }
233
234
    /**
235
     * Resizes an image.
236
     *
237
     * If one of the dimensions is set to `null`, another one is calculated automatically based on aspect ratio of
238
     * original image.
239
     *
240
     * If both of the dimensions are set then new dimensions are calculated so that image keeps aspect ratio.
241
     *
242
     * You can set $keepAspectRatio to false if you want to force fixed width and height.
243
     *
244
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
245
     * @param int $width the width in pixels
246
     * @param int $height the height in pixels
247
     * @param bool $keepAspectRatio should the image keep aspect ratio
248
     * @param bool $allowUpscaling should the image be upscaled if needed
249
     * @return ImageInterface
250
     */
251
    public static function resize($image, $width, $height, $keepAspectRatio = true, $allowUpscaling = false)
252
    {
253
        $img = self::ensureImageInterfaceInstance($image)->copy();
254
255
        /** @var BoxInterface $sourceBox */
256
        $sourceBox = $img->getSize();
257
        $destinationBox = static::getBox($sourceBox, $width, $height, $keepAspectRatio);
258
259
        if ($allowUpscaling === false && self::isUpscaling($sourceBox, $destinationBox)) {
260
            return $img;
261
        }
262
263
        return $img->resize($destinationBox);
264
    }
265
266
    /**
267
     * Adds a watermark to an existing image.
268
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
269
     * @param string|resource|ImageInterface $watermarkImage either ImageInterface, resource or a string containing watermark file path
270
     * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
271
     * @return ImageInterface
272
     * @throws Exception if `$start` is invalid
273
     */
274
    public static function watermark($image, $watermarkImage, array $start = [0, 0])
275
    {
276
        if (!isset($start[0], $start[1])) {
277
            throw new Exception('$start must be an array of two elements.');
278
        }
279
280
        $img = self::ensureImageInterfaceInstance($image);
281
        $watermark = self::ensureImageInterfaceInstance($watermarkImage);
282
        $img->paste($watermark, new Point($start[0], $start[1]));
283
284
        return $img;
285
    }
286
287
    /**
288
     * Draws a text string on an existing image.
289
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
290
     * @param string $text the text to write to the image
291
     * @param string $fontFile the file path or path alias
292
     * @param array $start the starting position of the text. This must be an array with two elements representing `x` and `y` coordinates.
293
     * @param array $fontOptions the font options. The following options may be specified:
294
     *
295
     * - color: The font color. Defaults to "fff".
296
     * - size: The font size. Defaults to 12.
297
     * - angle: The angle to use to write the text. Defaults to 0.
298
     *
299
     * @return ImageInterface
300
     * @throws Exception if `$fontOptions` is invalid
301
     */
302
    public static function text($image, $text, $fontFile, array $start = [0, 0], array $fontOptions = [])
303
    {
304
        if (!isset($start[0], $start[1])) {
305
            throw new Exception('$start must be an array of two elements.');
306
        }
307
308
        $fontSize = Arr::get($fontOptions, 'size', 12);
309
        $fontColor = Arr::get($fontOptions, 'color', 'fff');
310
        $fontAngle = Arr::get($fontOptions, 'angle', 0);
311
312
        $palette = new RGB();
313
        $color = $palette->color($fontColor);
314
315
        $img = self::ensureImageInterfaceInstance($image);
316
        $font = static::getImagine()->font($fontFile, $fontSize, $color);
317
318
        $img->draw()->text($text, $font, new Point($start[0], $start[1]), $fontAngle);
319
320
        return $img;
321
    }
322
323
    /**
324
     * Adds a frame around of the image. Please note that the image size will increase by `$margin` x 2.
325
     * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
326
     * @param int $margin the frame size to add around the image
327
     * @param string $color the frame color
328
     * @param int $alpha the alpha value of the frame.
329
     * @return ImageInterface
330
     */
331
    public static function frame($image, $margin = 20, $color = '666', $alpha = 100)
332
    {
333
        $img = self::ensureImageInterfaceInstance($image);
334
335
        $size = $img->getSize();
336
337
        $pasteTo = new Point($margin, $margin);
338
339
        $palette = new RGB();
340
        $color = $palette->color($color, $alpha);
341
342
        $box = new Box($size->getWidth() + ceil($margin * 2), $size->getHeight() + ceil($margin * 2));
343
344
        $finalImage = static::getImagine()->create($box, $color);
345
346
        $finalImage->paste($img, $pasteTo);
347
348
        return $finalImage;
349
    }
350
351
    /**
352
     * Returns box for a thumbnail to be created. If one of the dimensions is set to `null`, another one is calculated
353
     * automatically based on width to height ratio of original image box.
354
     *
355
     * @param BoxInterface $sourceBox original image box
356
     * @param int $width thumbnail width
357
     * @param int $height thumbnail height
358
     * @return BoxInterface thumbnail box
359
     */
360
    protected static function getThumbnailBox(BoxInterface $sourceBox, $width, $height)
361
    {
362
        if ($width !== null && $height !== null) {
0 ignored issues
show
The condition $height !== null is always true.
Loading history...
363
            return new Box($width, $height);
364
        }
365
366
        return self::getBox($sourceBox, $width, $height, false);
367
    }
368
369
    /**
370
     * Returns box for an image to be created.
371
     * If one of the dimensions is set to `null`, another one is calculated automatically based on width to height ratio
372
     * of original image box.
373
     *
374
     * If both of the dimensions are set then new dimensions are calculated so that image keeps aspect ratio.
375
     *
376
     * You can set $keepAspectRatio to false if you want to force fixed width and height.
377
     * @param BoxInterface $sourceBox
378
     * @param $width
379
     * @param $height
380
     * @param bool $keepAspectRatio
381
     * @return Box
382
     * @throws Exception
383
     */
384
    protected static function getBox(BoxInterface $sourceBox, $width, $height, $keepAspectRatio = true)
385
    {
386
        if ($width === null && $height === null) {
387
            throw new Exception('Width and height cannot be null at same time.');
388
        }
389
390
        $ratio = $sourceBox->getWidth() / $sourceBox->getHeight();
391
        if ($keepAspectRatio === false) {
392
            if ($height === null) {
393
                $height = ceil($width / $ratio);
394
            } elseif ($width === null) {
395
                $width = ceil($height * $ratio);
396
            }
397
        } else {
398
            if ($height === null) {
399
                $height = ceil($width / $ratio);
400
            } elseif ($width === null) {
401
                $width = ceil($height * $ratio);
402
            } elseif ($width / $height > $ratio) {
403
                $width = $height * $ratio;
404
            } else {
405
                $height = $width / $ratio;
406
            }
407
        }
408
409
        return new Box($width, $height);
410
    }
411
412
    /**
413
     * Checks if upscaling is going to happen
414
     * @param BoxInterface $sourceBox
415
     * @param BoxInterface $destinationBox
416
     * @return bool
417
     */
418
    protected static function isUpscaling(BoxInterface $sourceBox, BoxInterface $destinationBox)
419
    {
420
        return
421
            (
422
                $sourceBox->getWidth() <= $destinationBox->getWidth()
423
                && $sourceBox->getHeight() <= $destinationBox->getHeight()
424
            ) || (
425
                !$destinationBox->getWidth() && !$destinationBox->getHeight()
426
            );
427
    }
428
}
429