1 <?php
2 namespace Elboletaire\Watimage;
3
4 /**
5 * The Watermark class. It extends Image class so you can do anything you can
6 * do for images to your watermarks.
7 *
8 * @author Òscar Casajuana Alonso <elboletaire@underave.net>
9 * @copyright 2015 Òscar Casajuana <elboletaire at underave dot net>
10 * @link https://github.com/elboletaire/Watimage
11 * @license https://opensource.org/licenses/MIT MIT
12 */
13 class Watermark extends Image
14 {
15 /**
16 * Size param, used to resize the watermark.
17 *
18 * @var mixed Can either be string (`full` or a %) or the exact size (as integer or array)
19 */
20 protected $size;
21
22 /**
23 * Watermark margin.
24 *
25 * @var array
26 */
27 protected $margin = [0, 0];
28
29 /**
30 * Position for the watermark.
31 *
32 * @var mixed Can either be a string ala CSS or the exact position (as integer or array)
33 */
34 protected $position;
35
36 /**
37 * {@inheritdoc}
38 *
39 * @param string $file Filepath of the watermark to be loaded.
40 * @param array $options Array of options to be set, with keys: size,
41 * position and/or margin.
42 */
43 public function __construct($file = null, $options = [])
44 {
45 if (!empty($options)) {
46 foreach ($options as $option => $values) {
47 $method = 'set' . ucfirst($option);
48 if (!method_exists($this, $method)) {
49 continue;
50 }
51
52 $this->$method($values);
53 }
54 }
55
56 return parent::__construct($file);
57 }
58
59 /**
60 * {@inheritdoc}
61 *
62 * @return Watermark
63 */
64 public function destroy()
65 {
66 $this->size = $this->position = null;
67 $this->margin = [0, 0];
68
69 return parent::destroy();
70 }
71
72 /**
73 * Sets the position of the watermark.
74 *
75 * @param mixed $x Can be a position ala CSS, just position X or an array
76 * containing both params.
77 * @param int $y Position Y.
78 * @return Watermark
79 */
80 public function setPosition($x, $y = null)
81 {
82 $this->position = Normalize::cssPosition($x, $y);
83
84 return $this;
85 }
86
87 /**
88 * Sets the size of the watermark.
89 *
90 * This method has been added for backwards compatibility. If you wanna resize
91 * the watermark you can directly call ->resize from Watermark object.
92 *
93 * @param mixed $width Can be just width or an array containing both params.
94 * @param int $height Height.
95 * @return Watermark
96 */
97 public function setSize($width, $height = null)
98 {
99 $this->size = Normalize::watermarkSize($width, $height);
100
101 return $this;
102 }
103
104 /**
105 * Sets a margin for the watermark. Useful if you're using positioning ala CSS.
106 *
107 * @param mixed $x Can be just x position or an array containing both params.
108 * @param int $y Y position.
109 * @return Watermark
110 */
111 public function setMargin($x, $y = null)
112 {
113 $this->margin = Normalize::margin($x, $y);
114
115 return $this;
116 }
117
118 /**
119 * Applies the watermark to the given image.
120 *
121 * @param Image $image The image where apply the watermark.
122 * @return Image The resulting watermarked Image, so you can
123 * do $watermark->apply($image)->generate().
124 */
125 public function apply(Image $image)
126 {
127 $metadata = $image->getMetadata();
128 $this->calculateSize($metadata);
129 list($x, $y) = $this->calculatePosition($metadata);
130
131 $resource = $this->imagecreate($metadata['width'], $metadata['height']);
132
133 // @codingStandardsIgnoreStart
134 imagecopyresampled(
135 $resource, $image->getImage(),
136 0, 0, 0, 0,
137 $metadata['width'], $metadata['height'],
138 $metadata['width'], $metadata['height']
139 );
140 // @codingStandardsIgnoreEnd
141
142 imagealphablending($resource, true);
143 imagesavealpha($resource, false);
144
145 // @codingStandardsIgnoreStart
146 imagecopy(
147 $resource, $this->image,
148 $x, $y, 0, 0,
149 $this->width, $this->height
150 );
151 // @codingStandardsIgnoreEnd
152
153 $image->setImage($resource);
154
155 return $image;
156 }
157
158 /**
159 * Calculates the position of the watermark.
160 *
161 * @param array $metadata Image to be watermarked metadata.
162 * @return array Position in array x,y
163 */
164 protected function calculatePosition($metadata)
165 {
166 // Force center alignement if 'full' size has been set
167 if ($this->size == 'full') {
168 $this->position = 'center center';
169 }
170
171 if (is_array($this->position)) {
172 return $this->position;
173 }
174
175 if (empty($this->position)) {
176 $this->position = 'center center';
177 }
178
179 $x = $y = 0;
180
181 // Horizontal
182 if (preg_match('/right/', $this->position)) {
183 $x = $metadata['width'] - $this->width + $this->margin[0];
184 } elseif (preg_match('/left/', $this->position)) {
185 $x = 0 + $this->margin[0];
186 } elseif (preg_match('/center/', $this->position)) {
187 $x = $metadata['width'] / 2 - $this->width / 2 + $this->margin[0];
188 }
189
190 // Vertical
191 if (preg_match('/bottom/', $this->position)) {
192 $y = $metadata['height'] - $this->height + $this->margin[1];
193 } elseif (preg_match('/top/', $this->position)) {
194 $y = 0 + $this->margin[1];
195 } elseif (preg_match('/center/', $this->position)) {
196 $y = $metadata['height'] / 2 - $this->height / 2 + $this->margin[1];
197 }
198
199 return [$x, $y];
200 }
201
202 /**
203 * Calculates the required size for the watermark from $this->size.
204 *
205 * @param array $metadata Image metadata
206 * @return void
207 */
208 protected function calculateSize($metadata)
209 {
210 if (!isset($this->size)) {
211 return;
212 }
213
214 if (is_array($this->size)) {
215 list($width, $height) = $this->size;
216 if ($width == $this->width && $height == $this->height) {
217 return;
218 }
219 } elseif (preg_match('/[0-9]{1,3}%$/', $this->size)) {
220 $ratio = $this->size / 100;
221
222 $width = $this->width * $ratio;
223 $height = $this->height * $ratio;
224 } else {
225 // size == 'full'
226 $width = $this->width;
227 $height = $this->height;
228
229 if ($this->width > $metadata['width'] * 1.05 && $this->height > $metadata['height'] * 1.05) {
230 // both are already larger than the original by at least 5%...
231 // we need to make the watermark *smaller* for this one.
232 // where is the largest difference?
233 $wdiff = $width - $metadata['width'];
234 $hdiff = $height - $metadata['height'];
235 if ($wdiff > $hdiff) {
236 // the width has the largest difference - get percentage
237 $ratio = ($wdiff / $width) - 0.05;
238 } else {
239 $ratio = ($hdiff / $height) - 0.05;
240 }
241 $width -= $width * $ratio;
242 $height -= $height * $ratio;
243 } else {
244 // the watermark will need to be enlarged for this one
245 // where is the largest difference?
246 $wdiff = $metadata['width'] - $width;
247 $hdiff = $metadata['height'] - $height;
248 if ($wdiff > $hdiff) {
249 // the width has the largest difference - get percentage
250 $ratio = ($wdiff / $width) + 0.05;
251 } else {
252 $ratio = ($hdiff / $height) + 0.05;
253 }
254 $width += $width * $ratio;
255 $height += $height * $ratio;
256 }
257 }
258
259 $this->size = [$width, $height];
260 // Resize watermark to desired size
261 $this->classicResize($width, $height);
262 }
263 }
264