GDDrawer::drawPortrait()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace EaselDrawing\Drawers;
4
5
use EaselDrawing\Align;
6
use EaselDrawing\Canvas;
7
use EaselDrawing\Color;
8
use EaselDrawing\DrawerInterface;
9
use EaselDrawing\Elements\Image;
10
use EaselDrawing\Elements\Label;
11
use EaselDrawing\Elements\Line;
12
use EaselDrawing\Elements\Rectangle;
13
use EaselDrawing\Orientation;
14
use EaselDrawing\TextBackground;
15
16
class GDDrawer implements DrawerInterface
17
{
18
    /** @var resource Image created with GD */
19
    private $im;
20
21
    public function create(Canvas $canvas): string
22
    {
23
        $this->im = imagecreatetruecolor($canvas->getWidth(), $canvas->getHeight());
24
        $backgroundColor = $this->colorAlloc($canvas->getBackground());
25
        imagefilledrectangle($this->im, 0, 0, $canvas->getWidth(), $canvas->getHeight(), $backgroundColor);
26
27
        foreach ($canvas->getElements() as $element) {
28
            if ($element instanceof Rectangle) {
29
                $this->drawRectangle($element);
30
                continue;
31
            }
32
            if ($element instanceof Line) {
33
                $this->drawLine($element);
34
                continue;
35
            }
36
            if ($element instanceof Label) {
37
                $this->drawLabel($element);
38
                continue;
39
            }
40
            if ($element instanceof Image) {
41
                $this->drawImage($element);
42
                continue;
43
            }
44
            throw new \LogicException("Don't know what to do with element type " . get_class($element));
45
        }
46
        if ($canvas->getOrientation()->getValue() === Orientation::PORTRAIT) {
47
            $this->drawPortrait($backgroundColor);
48
        }
49
        if ($canvas->isGrayScale()) {
50
            imagefilter($this->im, IMG_FILTER_GRAYSCALE);
51
        }
52
        $filename = tempnam(sys_get_temp_dir(), '');
53
        imagepng($this->im, $filename);
54
        return $filename;
55
    }
56
57
    public function drawRectangle(Rectangle $rectangle)
58
    {
59
        $color = $this->colorAlloc($rectangle->getColor());
60
        imagesetthickness($this->im, $rectangle->getThickness());
61
        $x1 = $rectangle->getX();
62
        $x2 = $rectangle->getWidth() + $rectangle->getX();
63
        $y1 = $rectangle->getY();
64
        $y2 = $rectangle->getHeight() + $rectangle->getY();
65
        imageline($this->im, $x1, $y1, $x2, $y1, $color); // top
66
        imageline($this->im, $x2, $y1, $x2, $y2, $color); // left
67
        imageline($this->im, $x1, $y2, $x2, $y2, $color); // top
68
        imageline($this->im, $x1, $y1, $x1, $y2, $color); // right
69
    }
70
71
    public function drawLine(Line $line)
72
    {
73
        $color = $this->colorAlloc($line->getColor());
74
        imagesetthickness($this->im, $line->getThickness());
75
        imageline($this->im, $line->getX(), $line->getY(), $line->getX2(), $line->getY2(), $color);
76
    }
77
78
    public function drawLabel(Label $label)
79
    {
80
        $font = $label->getFont();
81
        $fontFile = $font->getFile();
82
        $fontSize = $label->getSize();
83
        $width = $label->getWidth();
84
        $height = $label->getHeight();
85
        if (intval(round($fontSize, 0)) < 4) {
86
            throw new \RuntimeException('The font size is too small to fit');
87
        }
88
89
        // check if the content fits in the width and height
90
        $box = imagettfbbox($fontSize, 0, $fontFile, $label->getContent());
91
        if ($box[2] > $width or abs($box[5]) > $height) {
92
            $reducedLabel = clone $label;
93
            $reducedLabel->setSize($reducedLabel->getSize() - 1);
94
            $this->drawLabel($reducedLabel);
95
            return;
96
        }
97
98
        // the text fit into the area
99
        $x = $label->getX();
100
        $y = $label->getY() + ceil(($height - $box[5]) / 2);
101
        $align = $label->getAlign()->getValue();
102 View Code Duplication
        if ($align === Align::RIGHT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
            $x = $label->getX() + $label->getWidth() - $box[2];
104
        }
105 View Code Duplication
        if ($align === Align::CENTER) { // center
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
            $x = $label->getX() + ceil(($label->getWidth() - $box[2]) / 2);
107
        }
108
109
        // draw text background
110
        $textBackground = $label->getTextBackground();
111
        if ($textBackground->getValue() === TextBackground::BOX) {
112
            imagefilledrectangle(
113
                $this->im,
114
                $label->getX(),
115
                $label->getY(),
116
                $label->getX() + $label->getWidth(),
117
                $label->getY() + $label->getHeight(),
118
                $this->colorAlloc($textBackground->getColor())
119
            );
120
        }
121
        if ($textBackground->getValue() === TextBackground::FIT) {
122
            imagefilledrectangle(
123
                $this->im,
124
                $x,
0 ignored issues
show
Bug introduced by
It seems like $x can also be of type double; however, parameter $x1 of imagefilledrectangle() 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

124
                /** @scrutinizer ignore-type */ $x,
Loading history...
125
                $y,
0 ignored issues
show
Bug introduced by
$y of type double is incompatible with the type integer expected by parameter $y1 of imagefilledrectangle(). ( Ignorable by Annotation )

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

125
                /** @scrutinizer ignore-type */ $y,
Loading history...
126
                $x + $box[2],
0 ignored issues
show
Bug introduced by
$x + $box[2] of type double is incompatible with the type integer expected by parameter $x2 of imagefilledrectangle(). ( Ignorable by Annotation )

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

126
                /** @scrutinizer ignore-type */ $x + $box[2],
Loading history...
127
                $y + $box[5],
0 ignored issues
show
Bug introduced by
$y + $box[5] of type double is incompatible with the type integer expected by parameter $y2 of imagefilledrectangle(). ( Ignorable by Annotation )

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

127
                /** @scrutinizer ignore-type */ $y + $box[5],
Loading history...
128
                $this->colorAlloc($textBackground->getColor())
129
            );
130
        }
131
132
        // draw text
133
        $color = $this->colorAlloc($label->getColor());
134
        imagettftext($this->im, $fontSize, 0, $x, $y, $color, $fontFile, $label->getContent());
0 ignored issues
show
Bug introduced by
It seems like $x 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

134
        imagettftext($this->im, $fontSize, 0, /** @scrutinizer ignore-type */ $x, $y, $color, $fontFile, $label->getContent());
Loading history...
Bug introduced by
$y 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

134
        imagettftext($this->im, $fontSize, 0, $x, /** @scrutinizer ignore-type */ $y, $color, $fontFile, $label->getContent());
Loading history...
135
    }
136
137
    public function drawPortrait(int $allocatedBackground)
138
    {
139
        $this->im = imagerotate($this->im, 90, $allocatedBackground);
140
    }
141
142
    public function drawImage(Image $image)
143
    {
144
        $filename = $image->getFilename();
145
        $destWidth = $image->getWidth();
146
        $destHeight = $image->getHeight();
147
        $destX = $image->getX();
148
        $destY = $image->getY();
149
150
        $ix = $this->imageCreateFromFile($filename);
151
152
        $srcWidth = imagesx($ix);
153
        $srcHeight = imagesy($ix);
154
        $destRatio = $destWidth / $destHeight;
155
        $sourceRadio = $srcWidth / $srcHeight;
156
        if ($sourceRadio > $destRatio) {
157
            $finalWidth = $destWidth;
158
            $padX = 0;
159
            $finalHeight = round($destWidth * $srcHeight / $srcWidth, 0);
160
            $padY = floor(($destHeight - $finalHeight) / 2);
161
        } else {
162
            $finalHeight = $destHeight;
163
            $padY = 0;
164
            $finalWidth = round($destHeight * $srcWidth / $srcHeight, 0);
165
            $padX = floor(($destWidth - $finalWidth) / 2);
166
        }
167
168
        // draw background if exists
169
        if ($image->hasBackground()) {
170
            $color = $this->colorAlloc($image->getBackground());
171
            imagefilledrectangle($this->im, $destX, $destY, $destX + $destWidth, $destY + $destHeight, $color);
172
        }
173
174
        // draw image
175
        $copy = imagecopyresized(
176
            $this->im,
177
            $ix,
178
            $destX + $padX,
0 ignored issues
show
Bug introduced by
$destX + $padX of type double is incompatible with the type integer expected by parameter $dst_x of imagecopyresized(). ( Ignorable by Annotation )

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

178
            /** @scrutinizer ignore-type */ $destX + $padX,
Loading history...
179
            $destY + $padY,
0 ignored issues
show
Bug introduced by
$destY + $padY of type double is incompatible with the type integer expected by parameter $dst_y of imagecopyresized(). ( Ignorable by Annotation )

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

179
            /** @scrutinizer ignore-type */ $destY + $padY,
Loading history...
180
            0,
181
            0,
182
            $finalWidth,
0 ignored issues
show
Bug introduced by
It seems like $finalWidth can also be of type double; however, parameter $dst_w of imagecopyresized() 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

182
            /** @scrutinizer ignore-type */ $finalWidth,
Loading history...
183
            $finalHeight,
0 ignored issues
show
Bug introduced by
It seems like $finalHeight can also be of type double; however, parameter $dst_h of imagecopyresized() 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

183
            /** @scrutinizer ignore-type */ $finalHeight,
Loading history...
184
            $srcWidth,
185
            $srcHeight
186
        );
187
        if (! $copy) {
188
            throw new \RuntimeException("Cannot import the image $filename");
189
        }
190
    }
191
192
    /**
193
     * Open an image as a GD resource
194
     *
195
     * @param string $filename
196
     * @return resource
197
     */
198
    protected function imageCreateFromFile(string $filename)
199
    {
200
        if (! file_exists($filename) || ! is_readable($filename)) {
201
            throw new \InvalidArgumentException("The file $filename does not exists or is not readable");
202
        }
203
        $filetype = (new \finfo())->file($filename, FILEINFO_MIME_TYPE);
204
        if ('image/jpeg' == $filetype) {
205
            return imagecreatefromjpeg($filename);
206
        }
207
        if ('image/png' == $filetype) {
208
            return imagecreatefrompng($filename);
209
        }
210
        if ('image/gif' == $filetype) {
211
            return imagecreatefromgif($filename);
212
        }
213
        if ('image/bmp' == $filetype) {
214
            return imagecreatefromwbmp($filename);
215
        }
216
        throw new \InvalidArgumentException("The file $filename is not a valid file type");
217
    }
218
219
    protected function colorAlloc(Color $color): int
220
    {
221
        return imagecolorallocate($this->im, ...$color->getRGB());
0 ignored issues
show
Bug introduced by
$color->getRGB() is expanded, but the parameter $red of imagecolorallocate() does not expect variable arguments. ( Ignorable by Annotation )

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

221
        return imagecolorallocate($this->im, /** @scrutinizer ignore-type */ ...$color->getRGB());
Loading history...
Bug introduced by
The call to imagecolorallocate() has too few arguments starting with green. ( Ignorable by Annotation )

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

221
        return /** @scrutinizer ignore-call */ imagecolorallocate($this->im, ...$color->getRGB());

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
222
    }
223
}
224