Passed
Push — master ( 0ca024...56f4a4 )
by Evgenii
01:30
created

src/Imagenator.php (9 issues)

Labels
Severity
1
<?php
2
3
namespace floor12\imagenator;
4
5
use floor12\imagenator\exception\FontNotFoundException;
6
use floor12\imagenator\exception\ImageNotFoundException;
7
use floor12\imagenator\exception\InvalidFontSizeException;
8
use floor12\imagenator\exception\InvalidHexColorException;
9
use floor12\imagenator\exception\InvalidPositionValueException;
10
use floor12\imagenator\exception\InvalidRowHeightException;
11
12
class Imagenator
13
{
14
    const DEFAULT_IMAGE = __DIR__ . '/../assets/default.png';
15
    /**
16
     * @var string Path to TTF font file
17
     */
18
    protected $font = __DIR__ . '/../assets/Rubik.ttf';
19
    /**
20
     * @var string
21
     */
22
    protected $text = "Dont forget to put some text here :-)";
23
    /**
24
     * @var false|resource
25
     */
26
    protected $image;
27
    /**
28
     * @var int
29
     */
30
    protected $imageWidth;
31
    /**
32
     * @var int
33
     */
34
    protected $imageHeight;
35
    /**
36
     * @var int
37
     */
38
    protected $rowHeight = 8;
39
    /**
40
     * @var int
41
     */
42
    protected $marginTopInPercents = 50;
43
    /**
44
     * @var int
45
     */
46
    protected $paddingLeftRightInPercents = 5;
47
    /**
48
     * @var int Default color is white
49
     */
50
    private $textColor = '16777215';
51
    /**
52
     * @var int
53
     */
54
    private $fontSizeInPercents = 5;
55
    /**
56
     * @var array
57
     */
58
    private $rows = [];
59
    /**
60
     * @var float|int
61
     */
62
    private $positionX;
63
    /**
64
     * @var float|int
65
     */
66
    private $positionStartY;
67
    /**
68
     * @var float|int
69
     */
70
    private $rowHeightInPx;
71
    /**
72
     * @var float|int
73
     */
74
    private $fontSizeInPx;
75
76
    /**
77
     * Imagenator constructor.
78
     * @param string $backgroundImagePath
79
     * @throws ImageNotFoundException
80
     */
81
    public function __construct(string $backgroundImagePath = self::DEFAULT_IMAGE)
82
    {
83
        $this->loadImage($backgroundImagePath);
84
    }
85
86
    /**
87
     * @param string $backgroundImagePath
88
     * @throws ImageNotFoundException
89
     */
90
    protected function loadImage(string $backgroundImagePath)
91
    {
92
        if (!file_exists($backgroundImagePath))
93
            throw new ImageNotFoundException();
94
        $this->image = imagecreatefrompng($backgroundImagePath);
95
        list($this->imageWidth, $this->imageHeight) = getimagesize($backgroundImagePath);
96
    }
97
98
    /**
99
     * @param string $resultImagePath
100
     * @return bool
101
     * @throws FontNotFoundException
102
     */
103
    public function generate(string $resultImagePath): bool
104
    {
105
        $this->calculateParams();
106
        $this->prepareRows();
107
        $this->putRowsToImage();
108
        return $this->saveImage($resultImagePath);
109
    }
110
111
    /**
112
     * @param string $pathToSave
113
     * @return bool
114
     */
115
    protected function saveImage(string $pathToSave): bool
116
    {
117
        return imagepng($this->image, $pathToSave) && imagedestroy($this->image);
0 ignored issues
show
It seems like $this->image can also be of type boolean; however, parameter $image of imagepng() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        return imagepng(/** @scrutinizer ignore-type */ $this->image, $pathToSave) && imagedestroy($this->image);
Loading history...
It seems like $this->image can also be of type boolean; however, parameter $image of imagedestroy() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        return imagepng($this->image, $pathToSave) && imagedestroy(/** @scrutinizer ignore-type */ $this->image);
Loading history...
118
    }
119
120
    protected function calculateParams()
121
    {
122
        $this->positionStartY = (int)($this->imageHeight / 100 * $this->marginTopInPercents);
123
        $this->rowHeightInPx = (int)($this->imageHeight / 100 * $this->rowHeight);
124
        $this->fontSizeInPx = (int)($this->imageHeight / 100 * $this->fontSizeInPercents);
125
        $this->positionX = (int)($this->imageWidth / 100 * $this->paddingLeftRightInPercents);
126
    }
127
128
    /**
129
     * @throws FontNotFoundException
130
     */
131
    protected function prepareRows(): void
132
    {
133
        if (!file_exists($this->font))
134
            throw new FontNotFoundException();
135
136
        $wordsArray = explode(' ', $this->text);
137
        $validRowWidth = $this->imageWidth - $this->positionX * 2;
138
        $currentRow = 0;
139
140
        foreach ($wordsArray as $word) {
141
            $testRow = isset($this->rows[$currentRow]) ? array_merge($this->rows[$currentRow], [$word]) : [$word];
142
            $imageBox = imagettfbbox($this->fontSizeInPx, 0, $this->font, implode('', $testRow));
143
            if ($imageBox[2] >= $validRowWidth) {
144
                $lastWord = end($this->rows[$currentRow]);
145
                if (mb_strlen($lastWord) <= 2) {
146
                    $this->rows[$currentRow + 1][] = array_pop($this->rows[$currentRow]);
147
                }
148
                $this->rows[++$currentRow][] = $word;
149
            } else {
150
                $this->rows[$currentRow][] = $word;
151
            }
152
        }
153
    }
154
155
    /**
156
     *
157
     */
158
    protected function putRowsToImage()
159
    {
160
        foreach ($this->rows as $rowNumber => $row) {
161
            $string = implode(' ', $row);
162
            $positionY = (int)$this->positionStartY + ($rowNumber * $this->rowHeightInPx);
163
            imagettftext($this->image, $this->fontSizeInPx, 0, $this->positionX, $positionY, $this->textColor, $this->font, $string);
0 ignored issues
show
$positionY of type double is incompatible with the type integer expected by parameter $y of imagettftext(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

163
            imagettftext($this->image, $this->fontSizeInPx, 0, $this->positionX, /** @scrutinizer ignore-type */ $positionY, $this->textColor, $this->font, $string);
Loading history...
It seems like $this->image can also be of type boolean; however, parameter $image of imagettftext() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

163
            imagettftext(/** @scrutinizer ignore-type */ $this->image, $this->fontSizeInPx, 0, $this->positionX, $positionY, $this->textColor, $this->font, $string);
Loading history...
It seems like $this->positionX can also be of type double; however, parameter $x of imagettftext() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

163
            imagettftext($this->image, $this->fontSizeInPx, 0, /** @scrutinizer ignore-type */ $this->positionX, $positionY, $this->textColor, $this->font, $string);
Loading history...
164
        }
165
    }
166
167
    /**
168
     * @param string $text
169
     * @return self
170
     */
171
    public
172
    function setText(string $text): self
173
    {
174
        $this->text = $text;
175
        return $this;
176
    }
177
178
    /**
179
     * @param string $font
180
     * @return self
181
     */
182
    public
183
    function setFont(string $font): self
184
    {
185
        $this->font = $font;
186
        return $this;
187
    }
188
189
    /**
190
     * @param string $colorInHex
191
     * @return self
192
     * @throws InvalidHexColorException
193
     */
194
    public
195
    function setColor(string $colorInHex): self
196
    {
197
        $colorInHex = str_replace('#', '', $colorInHex);
198
199
        if (!ctype_xdigit($colorInHex) || strlen($colorInHex) !== 6)
200
            throw new InvalidHexColorException();
201
202
        $red = hexdec(substr($colorInHex, 0, 2));
203
        $green = hexdec(substr($colorInHex, 2, 2));
204
        $blue = hexdec(substr($colorInHex, 4, 2));
205
        $this->textColor = imagecolorallocate($this->image, $red, $green, $blue);
0 ignored issues
show
It seems like $green can also be of type double; however, parameter $green of imagecolorallocate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
        $this->textColor = imagecolorallocate($this->image, $red, /** @scrutinizer ignore-type */ $green, $blue);
Loading history...
It seems like $red can also be of type double; however, parameter $red of imagecolorallocate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
        $this->textColor = imagecolorallocate($this->image, /** @scrutinizer ignore-type */ $red, $green, $blue);
Loading history...
It seems like $blue can also be of type double; however, parameter $blue of imagecolorallocate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
        $this->textColor = imagecolorallocate($this->image, $red, $green, /** @scrutinizer ignore-type */ $blue);
Loading history...
It seems like $this->image can also be of type boolean; however, parameter $image of imagecolorallocate() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
        $this->textColor = imagecolorallocate(/** @scrutinizer ignore-type */ $this->image, $red, $green, $blue);
Loading history...
206
        return $this;
207
    }
208
209
    /**
210
     * @param int $size
211
     * @return Imagenator
212
     * @throws InvalidFontSizeException
213
     */
214
    public
215
    function setFontSize(int $size)
216
    {
217
        if ($size < 1 || $size > 20)
218
            throw new InvalidFontSizeException();
219
        $this->fontSizeInPercents = $size;
220
        return $this;
221
    }
222
223
    /**
224
     * @param int $percent
225
     * @return Imagenator
226
     * @throws InvalidPositionValueException
227
     */
228
    public
229
    function setPadding(int $percent)
230
    {
231
        if ($percent < 1 || $percent > 100)
232
            throw new InvalidPositionValueException();
233
        $this->paddingLeftRightInPercents = $percent;
234
        return $this;
235
    }
236
237
    /**
238
     * @param int $percent
239
     * @return Imagenator
240
     * @throws InvalidPositionValueException
241
     */
242
    public function setMarginTopInPercents(int $percent)
243
    {
244
        if ($percent < 1 || $percent > 100)
245
            throw new InvalidPositionValueException();
246
        $this->marginTopInPercents = $percent;
247
        return $this;
248
    }
249
250
    /**
251
     * @param int $percent
252
     * @return Imagenator
253
     * @throws InvalidRowHeightException
254
     */
255
    public
256
    function setRowHeight(int $percent)
257
    {
258
        if ($percent < 1 || $percent > 30)
259
            throw new InvalidRowHeightException();
260
        $this->rowHeight = $percent;
261
        return $this;
262
    }
263
}
264