Automad
 All Classes Functions Variables Pages
image.php
1 <?php
2 /*
3  * ....
4  * .: '':.
5  * :::: ':..
6  * ::. ''..
7  * .:'.. ..':.:::' . :. '':.
8  * :. '' '' '. ::::.. ..:
9  * ::::. ..':.. .'''::::: .
10  * :::::::.. '..:::: :. :::: :
11  * ::'':::::::. ':::.'':.:::: :
12  * :.. ''::::::....': '':: :
13  * :::::. '::::: : .. '' .
14  * .''::::::::... ':::.'' ..'' :.''''.
15  * :..:::''::::: :::::...:'' :..:
16  * ::::::. ':::: :::::::: ..:: .
17  * ::::::::.:::: :::::::: :'':.:: .''
18  * ::: '::::::::.' ''::::: :.' '': :
19  * ::: :::::::::..' :::: ::...' .
20  * ::: .:::::::::: :::: :::: .:'
21  * '::' ''::::::: :::: : :: :
22  * ':::: :::: :'' .:
23  * :::: :::: ..''
24  * :::: ..:::: .:''
25  * '''' '''''
26  *
27  *
28  * AUTOMAD
29  *
30  * Copyright (c) 2014 by Marc Anton Dahmen
31  * http://marcdahmen.de
32  *
33  * Licensed under the MIT license.
34  * http://automad.org/license
35  */
36 
37 
38 namespace Automad\Core;
39 
40 
41 defined('AUTOMAD') or die('Direct access not permitted!');
42 
43 
52 class Image {
53 
58  private $originalFile;
59 
60 
66 
67 
73 
74 
79  private $requestedWidth;
80 
81 
87 
88 
93  private $crop;
94 
95 
100  private $cropX;
101 
102 
107  private $cropY;
108 
109 
114  private $type;
115 
116 
121  public $file;
122 
123 
128  private $fileFullPath;
129 
130 
135  public $description;
136 
137 
142  public $width;
143 
144 
149  public $height;
150 
151 
156  public function __construct($originalFile = false, $requestedWidth = false, $requestedHeight = false, $crop = false) {
157 
158  if ($originalFile) {
159 
160  $getimagesize = @getimagesize($originalFile);
161 
162  if ($getimagesize) {
163 
164  $this->originalFile = $originalFile;
165  $this->originalWidth = $getimagesize[0];
166  $this->originalHeight = $getimagesize[1];
167  $this->type = $getimagesize['mime'];
168  $this->description = $this->getDescription();
169  $this->crop = $crop;
170 
171  if ($requestedWidth) {
172  $this->requestedWidth = $requestedWidth;
173  } else {
174  $this->requestedWidth = $this->originalWidth;
175  }
176 
177  if ($requestedHeight) {
178  $this->requestedHeight = $requestedHeight;
179  } else {
180  $this->requestedHeight = $this->originalHeight;
181  }
182 
183  // Get the possible size for the generated image (based on crop and original size).
184  $this->calculateSize();
185 
186  // Get the filename hash, based on the given settings, to check later, if the file exists.
187  $this->file = $this->getImageCacheFilePath();
188  $this->fileFullPath = AM_BASE_DIR . $this->file;
189 
190  // Check if an image with the generated hash exists already and create the file, when neccassary.
191  $this->verifyCachedImage();
192 
193  }
194 
195  }
196 
197  }
198 
199 
204  private function calculateSize() {
205 
206  $originalAspect = $this->originalWidth / $this->originalHeight;
207  $requestedAspect = $this->requestedWidth / $this->requestedHeight;
208 
209  if ($this->crop) {
210 
211  // Crop image
212 
213  if ($originalAspect > $requestedAspect) {
214 
215  if ($this->requestedWidth < $this->originalWidth) {
216  $w = $this->requestedWidth;
217  } else {
218  $w = $this->originalWidth;
219  }
220 
221  $h = $w / $requestedAspect;
222 
223  if ($h > $this->originalHeight) {
224  $h = $this->originalHeight;
225  $requestedAspect = $w / $h;
226  }
227 
228  // crop X
229  $x = ($this->originalWidth - ($this->originalHeight * $requestedAspect)) / 2;
230  $y = 0;
231 
232 
233  } else {
234 
235  if ($this->requestedHeight < $this->originalHeight) {
236  $h = $this->requestedHeight;
237  } else {
238  $h = $this->originalHeight;
239  }
240 
241  $w = $h * $requestedAspect;
242 
243  if ($w > $this->originalWidth) {
244  $w = $this->originalWidth;
245  $requestedAspect = $w / $h;
246  }
247 
248  // crop X
249  $x = 0;
250  $y = ($this->originalHeight - ($this->originalWidth / $requestedAspect)) / 2;
251 
252  }
253 
254  } else {
255 
256  // No cropping
257 
258  $x = 0;
259  $y = 0;
260 
261  if ($originalAspect > $requestedAspect) {
262 
263  if ($this->requestedWidth < $this->originalWidth) {
264  $w = $this->requestedWidth;
265  } else {
266  $w = $this->originalWidth;
267  }
268 
269  $h = $w / $originalAspect;
270 
271  } else {
272 
273  if ($this->requestedHeight < $this->originalHeight) {
274  $h = $this->requestedHeight;
275  } else {
276  $h = $this->originalHeight;
277  }
278 
279  $w = $h * $originalAspect;
280 
281  }
282 
283  }
284 
285  $this->width = $w;
286  $this->height = $h;
287  $this->cropX = $x;
288  $this->cropY = $y;
289 
290  }
291 
292 
297  private function createImage() {
298 
299  switch($this->type){
300 
301  case 'image/jpeg':
302  $src = imagecreatefromjpeg($this->originalFile);
303  break;
304  case 'image/gif':
305  $src = imagecreatefromgif($this->originalFile);
306  break;
307  case 'image/png':
308  $src = imagecreatefrompng($this->originalFile);
309  break;
310  default:
311  $src = false;
312  break;
313 
314  }
315 
316  $dest = imagecreatetruecolor($this->width, $this->height);
317 
318  imagealphablending($dest, false);
319  imagesavealpha($dest, true);
320  imagecopyresampled($dest, $src, 0, 0, $this->cropX, $this->cropY, $this->width, $this->height, $this->originalWidth - (2 * $this->cropX), $this->originalHeight - (2 * $this->cropY));
321 
322  $old = umask(0);
323  Debug::log(umask(), 'Changed umask');
324  Debug::log($this, 'Saving "' . $this->fileFullPath . '"');
325 
326  // Create cache directory, if not existing.
327  if (!file_exists(AM_BASE_DIR . AM_DIR_CACHE_IMAGES)) {
328  mkdir(AM_BASE_DIR . AM_DIR_CACHE_IMAGES, 0777, true);
329  }
330 
331  switch($this->type){
332 
333  case 'image/jpeg':
334  imagejpeg($dest, $this->fileFullPath, AM_IMG_JPG_QUALITY);
335  break;
336  case 'image/gif':
337  imagegif($dest, $this->fileFullPath);
338  break;
339  case 'image/png':
340  imagepng($dest, $this->fileFullPath);
341  break;
342 
343  }
344 
345  umask($old);
346  Debug::log(umask(), 'Restored umask');
347 
348  ImageDestroy ($src);
349  ImageDestroy ($dest);
350 
351  }
352 
353 
360  private function getDescription() {
361 
362  if ($this->type == 'image/jpeg') {
363  $exif = @exif_read_data($this->originalFile);
364  if (array_key_exists('ImageDescription', $exif)) {
365  return $exif['ImageDescription'];
366  } else {
367  return '';
368  }
369  } else {
370  return '';
371  }
372 
373  }
374 
375 
386  private function getImageCacheFilePath() {
387 
388  $extension = strtolower(pathinfo($this->originalFile, PATHINFO_EXTENSION));
389 
390  // Create unique filename in the cache folder:
391  // The hash makes it possible to clearly identify an unchanged file in the cache,
392  // since the given hashData will always result in the same hash.
393  // So if a file gets requested, the hash is generated from the path, calculated width x height, the mtime from the original and the cropping setting.
394  $hashData = $this->originalFile . '-' . $this->width . 'x' . $this->height . '-' . filemtime($this->originalFile) . '-' . var_export($this->crop, true);
395  $hash = hash('md5', $hashData);
396 
397  $file = AM_DIR_CACHE_IMAGES . '/' . AM_FILE_PREFIX_CACHE . '_' . $hash . '.' . $extension;
398 
399  Debug::log($hashData, 'Hash data for "' . $hash . '"');
400 
401  return $file;
402 
403  }
404 
405 
411  private function verifyCachedImage() {
412 
413  if (!file_exists($this->fileFullPath)) {
414 
415  $this->createImage();
416 
417  }
418 
419  }
420 
421 
422 }
423 
424 
425 ?>
__construct($originalFile=false, $requestedWidth=false, $requestedHeight=false, $crop=false)
Definition: image.php:156
static log($element, $description= '')
Definition: debug.php:113