Passed
Push — master ( 56f4a4...b4d6f1 )
by Evgenii
02:32 queued 01:16
created

Imagenator   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 33
eloc 86
c 3
b 0
f 1
dl 0
loc 253
rs 9.76

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A saveImage() 0 3 2
A setFont() 0 5 1
A loadImage() 0 8 3
A setText() 0 5 1
A generate() 0 6 1
A calculateParams() 0 6 1
A prepareRows() 0 20 6
A setFontSize() 0 7 3
A setPadding() 0 7 3
A setRowHeight() 0 7 3
A setMarginTopInPercents() 0 6 3
A putRowsToImage() 0 6 2
A setColor() 0 13 3
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\ImageNotLoadedException;
8
use floor12\imagenator\exception\InvalidFontSizeException;
9
use floor12\imagenator\exception\InvalidHexColorException;
10
use floor12\imagenator\exception\InvalidPositionValueException;
11
use floor12\imagenator\exception\InvalidRowHeightException;
12
13
class Imagenator
14
{
15
    const DEFAULT_IMAGE = __DIR__ . '/../assets/default.png';
16
    /**
17
     * @var string Path to TTF font file
18
     */
19
    protected $font = __DIR__ . '/../assets/Rubik.ttf';
20
    /**
21
     * @var string
22
     */
23
    protected $text = "Dont forget to put some text here :-)";
24
    /**
25
     * @var resource
26
     */
27
    protected $image;
28
    /**
29
     * @var int
30
     */
31
    protected $imageWidth;
32
    /**
33
     * @var int
34
     */
35
    protected $imageHeight;
36
    /**
37
     * @var int
38
     */
39
    protected $rowHeight = 8;
40
    /**
41
     * @var int
42
     */
43
    protected $marginTopInPercents = 50;
44
    /**
45
     * @var int
46
     */
47
    protected $paddingLeftRightInPercents = 5;
48
    /**
49
     * @var int Default color is white
50
     */
51
    private $textColor = '16777215';
52
    /**
53
     * @var int
54
     */
55
    private $fontSizeInPercents = 5;
56
    /**
57
     * @var array
58
     */
59
    private $rows = [];
60
    /**
61
     * @var float|int
62
     */
63
    private $positionX;
64
    /**
65
     * @var float|int
66
     */
67
    private $positionStartY;
68
    /**
69
     * @var float|int
70
     */
71
    private $rowHeightInPx;
72
    /**
73
     * @var float|int
74
     */
75
    private $fontSizeInPx;
76
77
    /**
78
     * Imagenator constructor.
79
     * @param string $backgroundImagePath
80
     * @throws ImageNotFoundException
81
     */
82
    public function __construct(string $backgroundImagePath = self::DEFAULT_IMAGE)
83
    {
84
        $this->loadImage($backgroundImagePath);
85
    }
86
87
    /**
88
     * @param string $backgroundImagePath
89
     * @throws ImageNotFoundException
90
     * @throws ImageNotLoadedException
91
     */
92
    protected function loadImage(string $backgroundImagePath)
93
    {
94
        if (!file_exists($backgroundImagePath))
95
            throw new ImageNotFoundException();
96
        $this->image = imagecreatefrompng($backgroundImagePath);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatefrompng($backgroundImagePath) can also be of type false. However, the property $image is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
97
        if ($this->image == null)
98
            throw new ImageNotLoadedException();
99
        list($this->imageWidth, $this->imageHeight) = getimagesize($backgroundImagePath);
100
    }
101
102
    /**
103
     * @param string $resultImagePath
104
     * @return bool
105
     * @throws FontNotFoundException
106
     */
107
    public function generate(string $resultImagePath): bool
108
    {
109
        $this->calculateParams();
110
        $this->prepareRows();
111
        $this->putRowsToImage();
112
        return $this->saveImage($resultImagePath);
113
    }
114
115
    /**
116
     * @param string $pathToSave
117
     * @return bool
118
     */
119
    protected function saveImage(string $pathToSave): bool
120
    {
121
        return imagepng($this->image, $pathToSave) && imagedestroy($this->image);
122
    }
123
124
    protected function calculateParams()
125
    {
126
        $this->positionStartY = intval($this->imageHeight / 100 * $this->marginTopInPercents);
127
        $this->rowHeightInPx = intval($this->imageHeight / 100 * $this->rowHeight);
128
        $this->fontSizeInPx = intval($this->imageHeight / 100 * $this->fontSizeInPercents);
129
        $this->positionX = intval($this->imageWidth / 100 * $this->paddingLeftRightInPercents);
130
    }
131
132
    /**
133
     * @throws FontNotFoundException
134
     */
135
    protected function prepareRows(): void
136
    {
137
        if (!file_exists($this->font))
138
            throw new FontNotFoundException();
139
140
        $wordsArray = explode(' ', $this->text);
141
        $validRowWidth = $this->imageWidth - $this->positionX * 2;
142
        $currentRow = 0;
143
144
        foreach ($wordsArray as $word) {
145
            $testRow = isset($this->rows[$currentRow]) ? array_merge($this->rows[$currentRow], [$word]) : [$word];
146
            $imageBox = imagettfbbox($this->fontSizeInPx, 0, $this->font, implode('', $testRow));
147
            if ($imageBox[2] >= $validRowWidth) {
148
                $lastWord = end($this->rows[$currentRow]);
149
                if (mb_strlen($lastWord) <= 2) {
150
                    $this->rows[$currentRow + 1][] = array_pop($this->rows[$currentRow]);
151
                }
152
                $this->rows[++$currentRow][] = $word;
153
            } else {
154
                $this->rows[$currentRow][] = $word;
155
            }
156
        }
157
    }
158
159
    /**
160
     *
161
     */
162
    protected function putRowsToImage()
163
    {
164
        foreach ($this->rows as $rowNumber => $row) {
165
            $string = implode(' ', $row);
166
            $positionY = intval($this->positionStartY + ($rowNumber * $this->rowHeightInPx));
167
            imagettftext($this->image, $this->fontSizeInPx, 0, $this->positionX, $positionY, $this->textColor, $this->font, $string);
0 ignored issues
show
Bug introduced by
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

167
            imagettftext($this->image, $this->fontSizeInPx, 0, /** @scrutinizer ignore-type */ $this->positionX, $positionY, $this->textColor, $this->font, $string);
Loading history...
168
        }
169
    }
170
171
    /**
172
     * @param string $text
173
     * @return self
174
     */
175
    public
176
    function setText(string $text): self
177
    {
178
        $this->text = $text;
179
        return $this;
180
    }
181
182
    /**
183
     * @param string $font
184
     * @return self
185
     */
186
    public
187
    function setFont(string $font): self
188
    {
189
        $this->font = $font;
190
        return $this;
191
    }
192
193
    /**
194
     * @param string $colorInHex
195
     * @return self
196
     * @throws InvalidHexColorException
197
     */
198
    public
199
    function setColor(string $colorInHex): self
200
    {
201
        $colorInHex = str_replace('#', '', $colorInHex);
202
203
        if (!ctype_xdigit($colorInHex) || strlen($colorInHex) !== 6)
204
            throw new InvalidHexColorException();
205
206
        $red = intval(hexdec(substr($colorInHex, 0, 2)));
207
        $green = intval(hexdec(substr($colorInHex, 2, 2)));
208
        $blue = intval(hexdec(substr($colorInHex, 4, 2)));
209
        $this->textColor = imagecolorallocate($this->image, $red, $green, $blue);
210
        return $this;
211
    }
212
213
    /**
214
     * @param int $size
215
     * @return Imagenator
216
     * @throws InvalidFontSizeException
217
     */
218
    public
219
    function setFontSize(int $size)
220
    {
221
        if ($size < 1 || $size > 20)
222
            throw new InvalidFontSizeException();
223
        $this->fontSizeInPercents = $size;
224
        return $this;
225
    }
226
227
    /**
228
     * @param int $percent
229
     * @return Imagenator
230
     * @throws InvalidPositionValueException
231
     */
232
    public
233
    function setPadding(int $percent)
234
    {
235
        if ($percent < 1 || $percent > 100)
236
            throw new InvalidPositionValueException();
237
        $this->paddingLeftRightInPercents = $percent;
238
        return $this;
239
    }
240
241
    /**
242
     * @param int $percent
243
     * @return Imagenator
244
     * @throws InvalidPositionValueException
245
     */
246
    public function setMarginTopInPercents(int $percent)
247
    {
248
        if ($percent < 1 || $percent > 100)
249
            throw new InvalidPositionValueException();
250
        $this->marginTopInPercents = $percent;
251
        return $this;
252
    }
253
254
    /**
255
     * @param int $percent
256
     * @return Imagenator
257
     * @throws InvalidRowHeightException
258
     */
259
    public
260
    function setRowHeight(int $percent)
261
    {
262
        if ($percent < 1 || $percent > 30)
263
            throw new InvalidRowHeightException();
264
        $this->rowHeight = $percent;
265
        return $this;
266
    }
267
}
268