Watermark   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 64
dl 0
loc 193
ccs 0
cts 75
cp 0
rs 10
c 0
b 0
f 0
wmc 23

7 Methods

Rating   Name   Duplication   Size   Complexity  
B init() 0 12 7
A getPositionPoint() 0 5 1
A applyWatermark() 0 20 3
A getWatermark() 0 16 5
A getWatermarkBox() 0 7 1
A exec() 0 9 2
A getMarkFilepath() 0 13 4
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
        $image = $this->applyWatermark($image);
107
108
        return $image->get($file->getExtension() ?? Image::DEFAULT_EXTENSION);
109
    }
110
111
    /**
112
     * Get watermark image
113
     * @param ImagineInterface $imagine
114
     * @return ImageInterface|null
115
     * @throws \Imagine\Exception\RuntimeException
116
     * @throws \UnexpectedValueException
117
     * @throws \ErrorException
118
     */
119
    private function getWatermark(ImagineInterface $imagine): ?ImageInterface
120
    {
121
        $resource = null;
122
        if ($this->getMarkFilepath() !== null) {
123
            $resource = fopen($this->getMarkFilepath(), 'rb');
124
        } elseif (\is_string($this->markContent)) {
125
            $resource = 'a'; // TODO: create resource.
126
        } elseif (\is_resource($this->markContent)) {
127
            $resource = $this->markContent;
128
        }
129
130
        if ($resource === null) {
131
            return null;
132
        }
133
134
        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

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