ImageFile.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. <?php
  2. /**
  3. * @package Grav\Common\Page
  4. *
  5. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\Page\Medium;
  9. use Exception;
  10. use Grav\Common\Config\Config;
  11. use Grav\Common\Grav;
  12. use Gregwar\Image\Exceptions\GenerationError;
  13. use Gregwar\Image\Image;
  14. use Gregwar\Image\Source;
  15. use RocketTheme\Toolbox\Event\Event;
  16. use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
  17. use RuntimeException;
  18. use function array_key_exists;
  19. use function count;
  20. use function extension_loaded;
  21. use function in_array;
  22. /**
  23. * Class ImageFile
  24. * @package Grav\Common\Page\Medium
  25. *
  26. * @method Image applyExifOrientation($exif_orienation)
  27. */
  28. class ImageFile extends Image
  29. {
  30. /**
  31. * Destruct also image object.
  32. */
  33. #[\ReturnTypeWillChange]
  34. public function __destruct()
  35. {
  36. $adapter = $this->adapter;
  37. if ($adapter) {
  38. $adapter->deinit();
  39. }
  40. }
  41. /**
  42. * Clear previously applied operations
  43. *
  44. * @return void
  45. */
  46. public function clearOperations()
  47. {
  48. $this->operations = [];
  49. }
  50. /**
  51. * This is the same as the Gregwar Image class except this one fires a Grav Event on creation of new cached file
  52. *
  53. * @param string $type the image type
  54. * @param int $quality the quality (for JPEG)
  55. * @param bool $actual
  56. * @param array $extras
  57. * @return string
  58. */
  59. public function cacheFile($type = 'jpg', $quality = 80, $actual = false, $extras = [])
  60. {
  61. if ($type === 'guess') {
  62. $type = $this->guessType();
  63. }
  64. if (!$this->forceCache && !count($this->operations) && $type === $this->guessType()) {
  65. return $this->getFilename($this->getFilePath());
  66. }
  67. // Computes the hash
  68. $this->hash = $this->getHash($type, $quality, $extras);
  69. /** @var Config $config */
  70. $config = Grav::instance()['config'];
  71. // Seo friendly image names
  72. $seofriendly = $config->get('system.images.seofriendly', false);
  73. if ($seofriendly) {
  74. $mini_hash = substr($this->hash, 0, 4) . substr($this->hash, -4);
  75. $cacheFile = "{$this->prettyName}-{$mini_hash}";
  76. } else {
  77. $cacheFile = "{$this->hash}-{$this->prettyName}";
  78. }
  79. $cacheFile .= '.' . $type;
  80. // If the files does not exists, save it
  81. $image = $this;
  82. // Target file should be younger than all the current image
  83. // dependencies
  84. $conditions = array(
  85. 'younger-than' => $this->getDependencies()
  86. );
  87. // The generating function
  88. $generate = function ($target) use ($image, $type, $quality) {
  89. $result = $image->save($target, $type, $quality);
  90. if ($result !== $target) {
  91. throw new GenerationError($result);
  92. }
  93. Grav::instance()->fireEvent('onImageMediumSaved', new Event(['image' => $target]));
  94. };
  95. // Asking the cache for the cacheFile
  96. try {
  97. $perms = $config->get('system.images.cache_perms', '0755');
  98. $perms = octdec($perms);
  99. $file = $this->getCacheSystem()->setDirectoryMode($perms)->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
  100. } catch (GenerationError $e) {
  101. $file = $e->getNewFile();
  102. }
  103. // Nulling the resource
  104. $adapter = $this->getAdapter();
  105. $adapter->setSource(new Source\File($file));
  106. $adapter->deinit();
  107. if ($actual) {
  108. return $file;
  109. }
  110. return $this->getFilename($file);
  111. }
  112. /**
  113. * Gets the hash.
  114. *
  115. * @param string $type
  116. * @param int $quality
  117. * @param array $extras
  118. * @return string
  119. */
  120. public function getHash($type = 'guess', $quality = 80, $extras = [])
  121. {
  122. if (null === $this->hash) {
  123. $this->generateHash($type, $quality, $extras);
  124. }
  125. return $this->hash;
  126. }
  127. /**
  128. * Generates the hash.
  129. *
  130. * @param string $type
  131. * @param int $quality
  132. * @param array $extras
  133. */
  134. public function generateHash($type = 'guess', $quality = 80, $extras = [])
  135. {
  136. $inputInfos = $this->source->getInfos();
  137. $data = [
  138. $inputInfos,
  139. $this->serializeOperations(),
  140. $type,
  141. $quality,
  142. $extras
  143. ];
  144. $this->hash = sha1(serialize($data));
  145. }
  146. /**
  147. * Read exif rotation from file and apply it.
  148. */
  149. public function fixOrientation()
  150. {
  151. if (!extension_loaded('exif')) {
  152. throw new RuntimeException('You need to EXIF PHP Extension to use this function');
  153. }
  154. if (!in_array(exif_imagetype($this->source->getInfos()), [IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM], true)) {
  155. return $this;
  156. }
  157. // resolve any streams
  158. /** @var UniformResourceLocator $locator */
  159. $locator = Grav::instance()['locator'];
  160. $filepath = $this->source->getInfos();
  161. if ($locator->isStream($filepath)) {
  162. $filepath = $locator->findResource($this->source->getInfos(), true, true);
  163. }
  164. // Make sure file exists
  165. if (!file_exists($filepath)) {
  166. return $this;
  167. }
  168. try {
  169. $exif = @exif_read_data($filepath);
  170. } catch (Exception $e) {
  171. Grav::instance()['log']->error($filepath . ' - ' . $e->getMessage());
  172. return $this;
  173. }
  174. if ($exif === false || !array_key_exists('Orientation', $exif)) {
  175. return $this;
  176. }
  177. return $this->applyExifOrientation($exif['Orientation']);
  178. }
  179. }