1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Created by PhpStorm. |
4
|
|
|
* User: gseidel |
5
|
|
|
* Date: 11.11.17 |
6
|
|
|
* Time: 12:09 |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Enhavo\Bundle\MediaBundle\Filter\Filter; |
10
|
|
|
|
11
|
|
|
use Enhavo\Bundle\MediaBundle\Content\ContentInterface; |
12
|
|
|
use Enhavo\Bundle\MediaBundle\Exception\FilterException; |
13
|
|
|
use Enhavo\Bundle\MediaBundle\Filter\AbstractFilter; |
14
|
|
|
use Enhavo\Bundle\MediaBundle\Model\FilterSetting; |
15
|
|
|
use Imagine\Gd\Imagine; |
16
|
|
|
use Imagine\Image\Box; |
17
|
|
|
use Imagine\Image\ImageInterface; |
18
|
|
|
use Imagine\Image\Palette\RGB; |
19
|
|
|
use Imagine\Image\Point; |
20
|
|
|
use Imagine\Imagick\Imagine as ImagickImagine; |
21
|
|
|
|
22
|
|
|
class ImageFilter extends AbstractFilter |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* @param ContentInterface|\Enhavo\Bundle\MediaBundle\Model\FileInterface|\Enhavo\Bundle\MediaBundle\Model\FormatInterface $file |
26
|
|
|
* @param FilterSetting $setting |
27
|
|
|
* @throws FilterException |
28
|
|
|
*/ |
29
|
|
|
public function apply($file, FilterSetting $setting) |
30
|
|
|
{ |
31
|
|
|
$content = $this->getContent($file); |
32
|
|
|
|
33
|
|
|
$format = $this->getFormat($content, $setting); |
34
|
|
|
|
35
|
|
|
if ($format == 'gif' && class_exists('Imagick') && $this->isAnimatedGif($content->getContent())) { |
36
|
|
|
$imagine = new ImagickImagine(); |
37
|
|
|
$image = $imagine->load($content->getContent()); |
38
|
|
|
$image->layers()->coalesce(); |
39
|
|
|
$resultImage = $image->copy(); |
40
|
|
|
$resultImage = $this->resize($resultImage, $setting); |
41
|
|
|
$options = $this->getSaveOptions($format, $setting); |
42
|
|
|
$options['animated'] = true; |
43
|
|
|
$resultImage->save($content->getFilePath(), $options); |
44
|
|
|
|
45
|
|
|
} elseif($format == 'bmp' && class_exists('Imagick') ) { |
46
|
|
|
$imagine = new ImagickImagine(); |
47
|
|
|
$image = $imagine->load($content->getContent()); |
48
|
|
|
$resultImage = $image->copy(); |
49
|
|
|
$resultImage = $this->resize($resultImage, $setting); |
50
|
|
|
$resultImage->save($content->getFilePath(), $this->getSaveOptions($format, $setting)); |
51
|
|
|
|
52
|
|
|
} elseif(class_exists('Imagick') ) { |
53
|
|
|
$imagine = new ImagickImagine(); |
54
|
|
|
$imagine = $imagine->load($content->getContent()); |
55
|
|
|
$imagine = $this->resize($imagine, $setting); |
56
|
|
|
$imagine->save($content->getFilePath(), $this->getSaveOptions($format, $setting)); |
57
|
|
|
|
58
|
|
|
} else { |
59
|
|
|
$imagine = new Imagine(); |
60
|
|
|
$imagine = $imagine->load($content->getContent()); |
61
|
|
|
$imagine = $this->resize($imagine, $setting); |
62
|
|
|
$imagine->save($content->getFilePath(), $this->getSaveOptions($format, $setting)); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
$this->setMimeType($file, $this->getMimeTypeFromImageFormat($format)); |
66
|
|
|
$this->setExtension($file, $format); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @param $format |
71
|
|
|
* @param FilterSetting $setting |
72
|
|
|
* @return array |
73
|
|
|
*/ |
74
|
|
|
private function getSaveOptions($format, FilterSetting $setting) |
75
|
|
|
{ |
76
|
|
|
$options = []; |
77
|
|
|
|
78
|
|
|
$options['format'] = $format; |
79
|
|
|
if($setting->hasSetting('format')) { |
80
|
|
|
$options['format'] = $setting->getSetting('format'); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
if($setting->hasSetting('jpeg_quality')) { |
84
|
|
|
$options['jpeg_quality'] = $setting->getSetting('jpeg_quality'); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
if($setting->hasSetting('png_compression_level')) { |
88
|
|
|
$options['png_compression_level'] = $setting->getSetting('png_compression_level'); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
return $options; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
private function getFormat(ContentInterface $content, FilterSetting $setting) |
95
|
|
|
{ |
96
|
|
|
$format = $setting->getSetting('format'); |
97
|
|
|
if($format === null) { |
98
|
|
|
$format = $this->getImageFormat($content); |
99
|
|
|
if($format) { |
100
|
|
|
return $format; |
101
|
|
|
} |
102
|
|
|
return 'png'; |
103
|
|
|
} |
104
|
|
|
return $format; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @param ImageInterface $image |
109
|
|
|
* @param FilterSetting $setting |
110
|
|
|
* @return ImageInterface |
111
|
|
|
* @throws FilterException |
112
|
|
|
*/ |
113
|
|
|
private function resize(ImageInterface $image, FilterSetting $setting) |
114
|
|
|
{ |
115
|
|
|
if($setting->getSetting('cropHeight') !== null && |
116
|
|
|
$setting->getSetting('cropWidth') !== null && |
117
|
|
|
$setting->getSetting('cropY') !== null && |
118
|
|
|
$setting->getSetting('cropX') !== null |
119
|
|
|
) { |
120
|
|
|
$image = $this->crop( |
121
|
|
|
$image, |
122
|
|
|
$setting->getSetting('cropWidth'), |
123
|
|
|
$setting->getSetting('cropHeight'), |
124
|
|
|
$setting->getSetting('cropX'), |
125
|
|
|
$setting->getSetting('cropY'), |
126
|
|
|
$this->backgroundColorHexToByteArray($setting->getSetting('backgroundColor', 'FFFFFF')), |
127
|
|
|
$setting->getSetting('backgroundColorAlpha', 0) |
128
|
|
|
); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if($setting->getSetting('maxHeight') && $setting->getSetting('maxWidth') == null) { |
132
|
|
|
$image = $this->resizeWithMaxHeight($image, $setting->getSetting('maxHeight')); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
if($setting->getSetting('maxWidth') && $setting->getSetting('maxHeight') == null) { |
136
|
|
|
$image = $this->resizeWithMaxWidth($image, $setting->getSetting('maxWidth')); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
if($setting->getSetting('maxHeight') && $setting->getSetting('maxWidth')) { |
140
|
|
|
$image = $this->resizeWithMaxHeightAndWidth($image, $setting->getSetting('maxWidth'), $setting->getSetting('maxHeight')); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
if($setting->getSetting('height') && $setting->getSetting('width') == null) { |
144
|
|
|
$image = $this->scaleToHeight($image, $setting->getSetting('height')); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
if($setting->getSetting('width') && $setting->getSetting('height') == null) { |
148
|
|
|
$image = $this->scaleToWidth($image, $setting->getSetting('width')); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
if($setting->getSetting('height') && $setting->getSetting('width')) { |
152
|
|
|
$image = $this->resizeWithFixHeightAndWidth( |
153
|
|
|
$image, $setting->getSetting('width'), |
154
|
|
|
$setting->getSetting('height'), |
155
|
|
|
$this->mapResizeMode($setting->getSetting('mode', ImageInterface::THUMBNAIL_OUTBOUND)) |
156
|
|
|
); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
return $image; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
private function crop(ImageInterface $image, $width, $height, $x, $y, $backgroundColorValues, $backgroundColorAlpha) |
163
|
|
|
{ |
164
|
|
|
$palette = new RGB(); |
165
|
|
|
$backgroundColor = $palette->color($backgroundColorValues, $backgroundColorAlpha); |
166
|
|
|
|
167
|
|
|
if ($image instanceof \Imagine\Imagick\Image) { |
168
|
|
|
$imagine = new ImagickImagine(); |
169
|
|
|
} else { |
170
|
|
|
$imagine = new Imagine(); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
$size = $image->getSize(); |
174
|
|
|
// resize image for x position if necessary |
175
|
|
|
if($x < 0) { |
176
|
|
|
$newImage = $imagine->create(new Box($size->getWidth() + abs($x), $size->getHeight()), $backgroundColor); |
|
|
|
|
177
|
|
|
$image = $newImage->paste($image, new Point(abs($x), 0)); |
|
|
|
|
178
|
|
|
$x = 0; |
179
|
|
|
} elseif($x > $size->getWidth()) { |
180
|
|
|
$newImage = $imagine->create(new Box($x + 1, $size->getHeight()), $backgroundColor); |
181
|
|
|
$image = $newImage->paste($image, new Point(0, 0)); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$size = $image->getSize(); |
185
|
|
|
// resize image for y position if necessary |
186
|
|
|
if($y < 0) { |
187
|
|
|
$newImage = $imagine->create(new Box($size->getWidth(), $size->getHeight() + abs($y)), $backgroundColor); |
|
|
|
|
188
|
|
|
$image = $newImage->paste($image, new Point(0, abs($y))); |
|
|
|
|
189
|
|
|
$y = 0; |
190
|
|
|
} elseif($y > $size->getHeight()) { |
191
|
|
|
$newImage = $imagine->create(new Box($size->getWidth(), $y + 1), $backgroundColor); |
192
|
|
|
$image = $newImage->paste($image, new Point(0, 0)); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$size = $image->getSize(); |
196
|
|
|
// resize image if width is to big |
197
|
|
|
if(($x + $width) > $size->getWidth()) { |
198
|
|
|
$newImage = $imagine->create(new Box($x + $width, $size->getHeight()), $backgroundColor); |
199
|
|
|
$image = $newImage->paste($image, new Point(0, 0)); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$size = $image->getSize(); |
203
|
|
|
// resize image if height is to big |
204
|
|
|
if(($y + $height) > $size->getHeight()) { |
205
|
|
|
$newImage = $imagine->create(new Box($size->getWidth(), $y + $height + 1), $backgroundColor); |
206
|
|
|
$image = $newImage->paste($image, new Point(0, 0)); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
$image = $image->crop(new Point($x, $y), new Box($width, $height)); |
210
|
|
|
return $image; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
private function resizeWithFixHeightAndWidth(ImageInterface $image, $width, $height, $mode) |
214
|
|
|
{ |
215
|
|
|
if($image->getSize()->getHeight() < $height) { |
216
|
|
|
$image = $this->scaleToHeight($image, $height); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
if($image->getSize()->getWidth() < $width) { |
220
|
|
|
$image = $this->scaleToWidth($image, $width); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
$image = $image->thumbnail(new Box($width, $height), $mode); |
224
|
|
|
|
225
|
|
|
if($mode === ImageInterface::THUMBNAIL_OUTBOUND) { |
226
|
|
|
return $image; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
//inset my not have target size, so we resize |
230
|
|
|
$palette = new RGB(); |
231
|
|
|
$backgroundColor = $palette->color(array(255, 255, 255), 0); |
232
|
|
|
|
233
|
|
|
if ($image instanceof \Imagine\Imagick\Image) { |
234
|
|
|
$imagine = new ImagickImagine(); |
235
|
|
|
} else { |
236
|
|
|
$imagine = new Imagine(); |
237
|
|
|
} |
238
|
|
|
$newImage = $imagine->create(new Box($width, $height), $backgroundColor); |
239
|
|
|
$newImage->resize(new Box($width, $height)); |
240
|
|
|
|
241
|
|
|
$size = $image->getSize(); |
242
|
|
|
if($size->getHeight() !== $height) { |
243
|
|
|
$y = intval($height / 2) - intval($size->getHeight() / 2); |
244
|
|
|
$newImage->paste($image, new Point(0, $y)); |
245
|
|
|
} elseif($size->getWidth() !== $width) { |
246
|
|
|
$x = intval($width / 2) - intval($size->getWidth() / 2); |
247
|
|
|
$newImage->paste($image, new Point($x,0)); |
248
|
|
|
} else { |
249
|
|
|
return $image; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
return $newImage; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
private function resizeWithMaxHeightAndWidth(ImageInterface $image, $width, $height) |
256
|
|
|
{ |
257
|
|
|
if($image->getSize()->getHeight() > $height) { |
258
|
|
|
$image = $this->scaleToHeight($image, $height); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
if($image->getSize()->getWidth() > $width) { |
262
|
|
|
$image = $this->scaleToWidth($image, $width); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return $image; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
private function resizeWithMaxHeight(ImageInterface $image, $height) |
269
|
|
|
{ |
270
|
|
|
if($image->getSize()->getHeight() > $height) { |
271
|
|
|
$image = $this->scaleToHeight($image, $height); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
return $image; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
private function resizeWithMaxWidth(ImageInterface $image, $width) |
278
|
|
|
{ |
279
|
|
|
if($image->getSize()->getWidth() > $width) { |
280
|
|
|
$image = $this->scaleToWidth($image, $width); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
return $image; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
private function scaleToHeight(ImageInterface $image, $height) |
287
|
|
|
{ |
288
|
|
|
$width = $image->getSize()->getWidth() / $image->getSize()->getHeight() * $height; |
289
|
|
|
return $image->resize(new Box($width, $height)); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
private function scaleToWidth(ImageInterface $image, $width) |
293
|
|
|
{ |
294
|
|
|
$height = $width / ($image->getSize()->getWidth() / $image->getSize()->getHeight()); |
295
|
|
|
return $image->resize(new Box($width, $height)); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
private function isAnimatedGif($file) |
299
|
|
|
{ |
300
|
|
|
$count = preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $file, $m); |
301
|
|
|
return $count >= 2; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
private function mapResizeMode($mode) |
305
|
|
|
{ |
306
|
|
|
switch ($mode) { |
307
|
|
|
case 'inset': |
308
|
|
|
return ImageInterface::THUMBNAIL_INSET; |
309
|
|
|
case ImageInterface::THUMBNAIL_INSET: |
310
|
|
|
return ImageInterface::THUMBNAIL_INSET; |
311
|
|
|
default: |
312
|
|
|
return ImageInterface::THUMBNAIL_OUTBOUND; |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @param string $backgroundColorHex |
318
|
|
|
* @return array |
319
|
|
|
* @throws FilterException |
320
|
|
|
*/ |
321
|
|
|
private function backgroundColorHexToByteArray($backgroundColorHex) |
322
|
|
|
{ |
323
|
|
|
if (!preg_match('/^[0-9A-Fa-f]{6}$/', $backgroundColorHex)) { |
324
|
|
|
throw new FilterException('Imagefilter: Parameter "backgroundColor" must be color in hex format (e.g. "FFFFFF")'); |
325
|
|
|
} |
326
|
|
|
return sscanf($backgroundColorHex, "%02x%02x%02x"); |
|
|
|
|
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
public function getType() |
330
|
|
|
{ |
331
|
|
|
return 'image'; |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
|