Passed
Push — master ( d21985...736875 )
by Kanstantsin
08:26
created

Watermark::applyWatermark()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 16
ccs 0
cts 10
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace tkanstantsin\fileupload\formatter\adapter;
5
6
use Imagine\Image\BoxInterface;
7
use Imagine\Image\ImageInterface;
8
use Imagine\Image\ImagineInterface;
9
use Imagine\Image\Point;
10
use tkanstantsin\fileupload\config\InvalidConfigException;
11
use tkanstantsin\fileupload\formatter\Image;
12
use tkanstantsin\fileupload\formatter\ImagineFactory;
13
use tkanstantsin\fileupload\model\IFile;
14
15
/**
16
 * Class Watermark
17
 */
18
class Watermark extends AbstractImageAdapter
19
{
20
    public const POSITION_CENTER_CENTER = 'center_center';
21
    public const POSITION_CENTER_LEFT = 'center_left';
22
    public const POSITION_CENTER_RIGHT = 'center_right';
23
    public const POSITION_TOP_CENTER = 'top_center';
24
    public const POSITION_TOP_LEFT = 'top_left';
25
    public const POSITION_TOP_RIGHT = 'top_right';
26
    public const POSITION_BOTTOM_CENTER = 'bottom_center';
27
    public const POSITION_BOTTOM_LEFT = 'bottom_left';
28
    public const POSITION_BOTTOM_RIGHT = 'bottom_right';
29
30
    public const SIZE_COVER = 'cover';
31
    public const SIZE_CONTAIN = 'contain';
32
    public const SIZE_STRETCH = 'stretch';
33
34
    /**
35
     * Absolute path to image file
36
     * @var callable|string|null
37
     */
38
    public $markFilepath;
39
    /**
40
     * Water mark image binary
41
     * @var string|resource|null
42
     */
43
    public $markContent;
44
45
    /**
46
     * Opacity for watermark image
47
     * @var int
48
     */
49
    public $opacity = 100;
50
    /**
51
     * TODO: implement position.
52
     * @var string
53
     */
54
    public $position = self::POSITION_CENTER_CENTER;
55
    /**
56
     * TODO: implement size.
57
     * @var string
58
     */
59
    public $size = self::SIZE_CONTAIN;
60
    /**
61
     * Watermark size relative to image size
62
     * @var float
63
     */
64
    public $scale = 0.9;
65
66
    /**
67
     * @inheritdoc
68
     * @throws \tkanstantsin\fileupload\config\InvalidConfigException
69
     * @throws \ErrorException
70
     */
71
    public function init(): void
72
    {
73
        parent::init();
74
75
        if ($this->scale > 1 || $this->scale <= 0) {
76
            throw new InvalidConfigException('Scale must be greater than 0 and lower or equal than 1.');
77
        }
78
        if ($this->markFilepath === null && $this->markContent === null) {
79
            throw new InvalidConfigException('Either watermarkPath or watermarkImage must be defined.');
80
        }
81
        if ($this->getMarkFilepath() !== null && !file_exists($this->getMarkFilepath())) {
82
            throw new InvalidConfigException(sprintf('Watermark by path `%s` not found.', $this->getMarkFilepath()));
83
        }
84
    }
85
86
    /**
87
     * Applies filters or something to content and return it
88
     *
89
     * @param IFile $file
90
     * @param       $content
91
     *
92
     * @return mixed
93
     * @throws \Imagine\Exception\OutOfBoundsException
94
     * @throws \UnexpectedValueException
95
     * @throws \Imagine\Exception\InvalidArgumentException
96
     * @throws \Imagine\Exception\RuntimeException
97
     * @throws \ImageOptimizer\Exception\Exception
98
     * @throws \ErrorException
99
     */
100
    public function exec(IFile $file, $content)
101
    {
102
        $image = \is_resource($content)
103
            ? $this->imagine->read($content)
104
            : $this->imagine->load($content);
105
106
        if ($this->markFilepath !== null || $this->markContent !== null) {
107
            $image = $this->applyWatermark($image);
108
        }
109
110
        return $image->get($file->getExtension() ?? Image::DEFAULT_EXTENSION);
111
    }
112
113
    /**
114
     * Get watermark image
115
     * @param ImagineInterface $imagine
116
     * @return ImageInterface
117
     * @throws \Imagine\Exception\RuntimeException
118
     * @throws \UnexpectedValueException
119
     * @throws \ErrorException
120
     */
121
    private function getWatermark(ImagineInterface $imagine): ImageInterface
122
    {
123
        $resource = null;
124
        if ($this->getMarkFilepath() !== null) {
125
            $resource = fopen($this->getMarkFilepath(), 'rb');
126
        } elseif (\is_string($this->markContent)) {
127
            $resource = 'a'; // TODO: create resource.
128
        } elseif (\is_resource($this->markContent)) {
129
            $resource = $this->markContent;
130
        }
131
132
        if ($resource === null) {
133
            throw new \UnexpectedValueException('Water mark image invalid format');
134
        }
135
136
        return $imagine->read($resource);
0 ignored issues
show
Bug introduced by
It seems like $resource can also be of type false and string; however, parameter $resource of Imagine\Image\ImagineInterface::read() 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

136
        return $imagine->read(/** @scrutinizer ignore-type */ $resource);
Loading history...
137
    }
138
139
    /**
140
     * @param BoxInterface $imageSize
141
     * @param BoxInterface $watermarkSize
142
     *
143
     * @return BoxInterface
144
     */
145
    private function getWatermarkBox(BoxInterface $imageSize, BoxInterface $watermarkSize): BoxInterface
146
    {
147
        // ensure that watermark smaller than image
148
        $watermarkSize = $watermarkSize->widen((int) ($imageSize->getWidth() * $this->scale));
149
        $watermarkSize = $watermarkSize->heighten(min($watermarkSize->getHeight(), $imageSize->getHeight()));
150
151
        return $watermarkSize;
152
    }
153
154
    /**
155
     * @param BoxInterface $imageSize
156
     * @param BoxInterface $watermarkSize
157
     *
158
     * @return Point
159
     * @throws \Imagine\Exception\InvalidArgumentException
160
     */
161
    private function getPositionPoint(BoxInterface $imageSize, BoxInterface $watermarkSize): Point
162
    {
163
        return new Point(
164
            (int) (($imageSize->getWidth() - $watermarkSize->getWidth()) / 2),
165
            (int) (($imageSize->getHeight() - $watermarkSize->getHeight()) / 2)
166
        );
167
    }
168
169
    /**
170
     * @return null|string
171
     * @throws \ErrorException
172
     */
173
    private function getMarkFilepath(): ?string
174
    {
175
        if ($this->markFilepath === null) {
176
            return null;
177
        }
178
        if (\is_string($this->markFilepath)) {
179
            return $this->markFilepath;
180
        }
181
        if (\is_callable($this->markFilepath)) {
182
            return \call_user_func($this->markFilepath, $this);
183
        }
184
185
        throw new \ErrorException('Mark file path has invalid format. Callable, string or null expected.');
186
    }
187
188
    /**
189
     * @param ImageInterface $image
190
     * @return ImageInterface
191
     * @throws \ErrorException
192
     */
193
    private function applyWatermark(ImageInterface $image): ImageInterface
194
    {
195
        $imageSize = $image->getSize();
196
197
        $watermark = $this->getWatermark($this->imagine);
198
        $watermarkSize = $watermark->getSize();
199
200
        // NOTE: only for position: center
201
        $watermarkSize = $this->getWatermarkBox($imageSize, $watermarkSize);
202
203
        /* @see http://urmaul.com/blog/imagick-filters-comparison */
204
        $filter = $this->driver === ImagineFactory::IMAGICK ? ImageInterface::FILTER_SINC : ImageInterface::FILTER_UNDEFINED;
205
        $watermark = $watermark->resize($watermarkSize, $filter);
206
        $watermarkSize = $watermark->getSize();
207
208
        return $image->paste($watermark, $this->getPositionPoint($imageSize, $watermarkSize));
209
    }
210
}