Passed
Push — master ( 6356e4...b6192d )
by Kanstantsin
02:31
created

Image::format()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace tkanstantsin\fileupload\formatter;
5
6
use Imagine\Image\Box;
7
use Imagine\Image\BoxInterface;
8
use Imagine\Image\ImageInterface;
9
use Imagine\Image\Palette\RGB as RGBPalette;
10
use Imagine\Image\Point;
11
use Imagine\Imagick\Imagine;
12
13
/**
14
 * Class ImageProcessor
15
 */
16
class Image extends File
17
{
18
    /**
19
     * Like background `cover` in css.
20
     */
21
    public const RESIZE_OUTBOUND = \Imagine\Image\ImageInterface::THUMBNAIL_OUTBOUND;
22
    /**
23
     * Like background `contain` in css.
24
     */
25
    public const RESIZE_INSET = \Imagine\Image\ImageInterface::THUMBNAIL_INSET;
26
    /**
27
     * Means that image may be smaller than defined in config, never bigger.
28
     */
29
    public const RESIZE_INSET_KEEP_RATIO = 'inset_keep_ratio';
30
31
    public const DEFAULT_EXTENSION = 'jpg';
32
33
    /**
34
     * @var int
35
     */
36
    public $width;
37
    /**
38
     * @var int
39
     */
40
    public $height;
41
42
    /**
43
     * Used when defined only height as upper limit for width
44
     * @todo: implement in Image::createBox() method.
45
     * @var int
46
     */
47
    public $maxWidth;
48
    /**
49
     * Used when defined only widith as upper limit for height
50
     * @var int
51
     */
52
    public $maxHeight;
53
54
    /**
55
     * Used for jpg images which may be png originally and have transparency.
56
     * @var string
57
     */
58
    public $transparentBackground = 'ffffff';
59
60
    /**
61
     * @var string
62
     */
63
    public $mode = self::RESIZE_INSET;
64
    /**
65
     * Whether image must keep aspect ration when used inset mote.
66
     * Means that image would may be smaller than smaller
67
     * @var bool
68
     */
69
    public $keepRatio = true;
70
71
    /**
72
     * @var Imagine
73
     */
74
    protected $imagine;
75
76
    /**
77
     * @inheritdoc
78
     * @throws \Imagine\Exception\RuntimeException
79
     */
80
    public function init(): void
81
    {
82
        parent::init();
83
84
        $this->imagine = new Imagine();
85
    }
86
87
    /**
88
     * @inheritdoc
89
     * @throws \UnexpectedValueException
90
     * @throws \Imagine\Exception\InvalidArgumentException
91
     * @throws \Imagine\Exception\RuntimeException
92
     */
93
    protected function getContentInternal()
94
    {
95
        $image = $this->imagine->read(parent::getContentInternal());
96
        $image = $this->format($image);
97
98
        return $image->get($this->getExtension());
99
    }
100
101
    /**
102
     * @return string
103
     */
104
    protected function getExtension(): string
105
    {
106
        return mb_strtolower($this->file->getExtension() ?? self::DEFAULT_EXTENSION);
107
    }
108
109
    /**
110
     * @param ImageInterface $image
111
     * @return ImageInterface
112
     * @throws \UnexpectedValueException
113
     * @throws \Imagine\Exception\RuntimeException
114
     * @throws \Imagine\Exception\InvalidArgumentException
115
     */
116
    protected function format(ImageInterface $image): ImageInterface
117
    {
118
        $image = $this->resize($image);
119
        $image = $this->setBackground($image);
120
121
        return $image;
122
    }
123
124
    /**
125
     * @param ImageInterface $image
126
     * @return ImageInterface
127
     * @throws \Imagine\Exception\RuntimeException
128
     * @throws \UnexpectedValueException
129
     * @throws \Imagine\Exception\InvalidArgumentException
130
     */
131
    protected function resize(ImageInterface $image): ImageInterface
132
    {
133
        $box = $this->createBox($image->getSize());
134
        if ($box === null) {
135
            return $image;
136
        }
137
138
        switch ($this->mode) {
139
            case self::RESIZE_OUTBOUND:
140
            case self::RESIZE_INSET:
141
                return $image->thumbnail($box, $this->mode);
142
            case self::RESIZE_INSET_KEEP_RATIO:
143
                // TODO: implement new resize mode.
144
                throw new \UnexpectedValueException(sprintf('Resize mode `%s` not supported yet', $this->mode));
145
            default:
146
                throw new \UnexpectedValueException(sprintf('Image resize mode `%s` not defined', $this->mode));
147
        }
148
    }
149
150
    /**
151
     * @param BoxInterface $actualBox
152
     * @return BoxInterface|null
153
     * @throws \Imagine\Exception\InvalidArgumentException
154
     */
155
    protected function createBox(BoxInterface $actualBox): ?BoxInterface
156
    {
157
        if ($this->width !== null
158
            && $this->height !== null
159
        ) {
160
            return new Box($this->width, $this->height);
161
        }
162
163
        if ($this->width !== null) {
164
            $box = $actualBox->widen($this->width);
165
            if ($this->maxHeight !== null && $this->maxHeight < $box->getHeight()) {
166
                $box = $box->heighten($this->maxHeight);
167
            }
168
169
            return $box;
170
        }
171
        if ($this->height !== null) {
172
            $box = $actualBox->heighten($this->height);
173
            if ($this->maxWidth !== null && $this->maxWidth < $box->getWidth()) {
174
                $box = $box->widen($this->maxWidth);
175
            }
176
177
            return $box;
178
        }
179
180
        // both are null
181
        return null;
182
    }
183
184
    /**
185
     * Add Image::transparentBackground color behind image.
186
     * If original image was of PNG type but stored with jpg extension, or it
187
     * must be converted
188
     *
189
     * @param ImageInterface $image
190
     * @return ImageInterface
191
     * @throws \Imagine\Exception\RuntimeException
192
     * @throws \Imagine\Exception\InvalidArgumentException
193
     */
194
    protected function setBackground(ImageInterface $image): ImageInterface
195
    {
196
        if (!\in_array($this->getExtension(), ['jpg', 'jpeg'], true)) {
197
            return $image;
198
        }
199
200
        $palette = new RGBPalette();
201
        $backgroundColor = $palette->color($this->transparentBackground, 100);
202
        $background = $this->imagine->create($image->getSize(), $backgroundColor);
203
204
        return $background->paste($image, new Point(0, 0));
205
    }
206
}