diff --git a/wire/core/ImageSizer.php b/wire/core/ImageSizer.php index 9d0104f8..324c9ce1 100755 --- a/wire/core/ImageSizer.php +++ b/wire/core/ImageSizer.php @@ -11,7 +11,7 @@ * * Other user contributions as noted. * - * Copyright (C) 2016 by Horst Nogajski and Ryan Cramer + * Copyright (C) 2016-2019 by Horst Nogajski and Ryan Cramer * This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/ * * @method bool resize($targetWidth, $targetHeight = 0) @@ -115,15 +115,21 @@ class ImageSizer extends Wire { self::$knownEngines = array(); $modules = $this->wire('modules'); - $engines = $modules->find("className^=ImageSizerEngine"); + $engines = $modules->findByPrefix('ImageSizerEngine'); + $numEngines = count($engines); - foreach($engines as $module) { - $moduleName = $module->className(); + foreach($engines as $moduleName) { if(!$modules->isInstalled($moduleName)) continue; - if(count($engines) > 1) { - $configData = $modules->getModuleConfigData($moduleName); + if($numEngines > 1) { + $configData = $modules->getConfig($moduleName); $priority = isset($configData['enginePriority']) ? (int) $configData['enginePriority'] : 0; - while(isset(self::$knownEngines[$priority])) $priority++; + // multiply by 10 to ensure two priority 1 engines don't get mixed up with a priority 2 engine + // for instance, two priority 1 engines become 10 and 11, rather than 1 and 2, as a priority 1 + // engine incremented to 2 could otherwise be confused with a priority 2 engine + $priority *= 10; + while(isset(self::$knownEngines[$priority])) { + $priority++; + } } else { $priority = 0; } @@ -131,9 +137,61 @@ class ImageSizer extends Wire { } if(count(self::$knownEngines) > 1) ksort(self::$knownEngines); + self::$knownEngines[] = $this->defaultEngineName; return self::$knownEngines; } + + /** + * Get array of information for all ImageSizer engines (or optionally a specific ImageSizer engine) + * + * Returns array of arrays indexed by engine name, each with the following: + * + * - `name` (string): engine name + * - `title` (string): engine title + * - `class` (string): PHP class name for engine + * - `summary` (string): Single sentence summary of the engine + * - `author` (string): Authr name (if available) + * - `moduleVersion` (string): Version of the module that powers this engine + * - `libraryVersion` (string): Version of the library that powers this engine + * - `sources` (array): Supported formats for source images it reads (i.e. JPG, JPEG, PNG, PNG24, GIF, GIF87, etc.) + * - `targets` (array): Supported formats for target images it creates (i.e. JPG, PNG, PNG24, WEBP, etc.) + * - `quality` (int): Current quality setting configured with the engine + * - `sharpening` (string): Current sharpening setting configured with the engine + * - `priority` (int): Engine priority (lower is higher priority) + * - `runOrder` (int): Order ImageSizer will try this engine in relative to others (lower runs first), derived from priority. + * + * @param string $name Specify engine name to get info just for that engine or omit to get info for all engines (default) + * @return array Array of arrays indexed by engine name, or if $name specified then just array of info for that engine. + * Returns empty array on error. + * @since 3.0.138 + * + */ + public function getEngineInfo($name = '') { + + $infos = array(); + $engineNames = $name ? array($name) : $this->getEngines(); + $prefix = 'ImageSizerEngine'; + + if($name && stripos($name, $prefix) === 0) { + $name = str_replace($prefix, '', $name); + } + + foreach($engineNames as $priority => $engineName) { + $shortName = str_replace($prefix, '', $engineName); + if($name && $shortName !== $name) continue; + $engine = $this->getEngine($engineName); + if(!$engine) continue; + $info = $engine->getEngineInfo(); + $info['runOrder'] = $priority; + $infos[$shortName] = $info; + } + + // if one engine requested reduce array to just that engine + if($name) $infos = isset($infos[$name]) ? $infos[$name] : array(); + + return $infos; + } /** * Instantiate an ImageSizerEngine @@ -161,20 +219,13 @@ class ImageSizer extends Wire { $engine = null; $bestFallbackEngine = null; // first engine that was supported but failed webp check $engineNames = $this->getEngines(); - $engineNames[] = $this->defaultEngineName; // find first supported engine, according to knownEngines priority foreach($engineNames as $engineName) { if($this->forceEngineName && $engineName != $this->forceEngineName) continue; - if($engineName === $this->defaultEngineName) { - $engineClass = __NAMESPACE__ . "\\$engineName"; - $e = $this->wire(new $engineClass()); - } else { - $e = $this->wire('modules')->get($engineName); - } - + $e = $this->getEngine($engineName); if(!$e) continue; /** @var ImageSizerEngine $e */ @@ -365,11 +416,24 @@ class ImageSizer extends Wire { /** * Get the current ImageSizerEngine * - * @return ImageSizerEngine + * @param string $engineName Optionally specify a specific engine name to get a new instance of that engine + * When used, returned engine is in an unprepared state (no filename assigned, etc.). Since 3.0.138. + * @return ImageSizerEngine|null Returns ImageSizerEngine or null only if requested $engineName is not found. + * If no $engineName is specified this method may return an existing instance from a previous call. * @throws WireException * */ - public function getEngine() { + public function getEngine($engineName = '') { + + if($engineName) { + if($engineName === $this->defaultEngineName) { + $engineClass = __NAMESPACE__ . "\\$engineName"; + $engine = $this->wire(new $engineClass()); + } else { + $engine = $this->wire('modules')->get($engineName); + } + return $engine; + } if($this->engine) return $this->engine; diff --git a/wire/core/ImageSizerEngine.php b/wire/core/ImageSizerEngine.php index 599629e2..d9743116 100755 --- a/wire/core/ImageSizerEngine.php +++ b/wire/core/ImageSizerEngine.php @@ -486,6 +486,64 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable return $this->validSourceImageFormats(); } + /** + * Get an array of image file formats this ImageSizerModule can use as source or target + * + * Unless using the $type argument, returned array contains 'source' and 'target' indexes, + * each an array of image file types/extensions in uppercase. + * + * @param string $type Specify 'source' or 'target' to get just those formats, or omit to get all. + * @return array + * @since 3.0.138 + * + */ + public function getSupportedFormats($type = '') { + $a = array( + 'source' => $this->validSourceImageFormats(), + 'target' => $this->validTargetImageFormats() + ); + return $type && isset($a[$type]) ? $a[$type] : $a; + } + + /** + * Get array of information about this engine + * + * @return array + * @since 3.0.138 + * + */ + public function getEngineInfo() { + + $formats = $this->getSupportedFormats(); + $moduleName = $this->className(); + $className = $this->className(true); + + if(is_callable("$className::getModuleInfo")) { + $moduleInfo = $className::getModuleInfo(); + } else { + $moduleInfo = $this->wire('modules')->getModuleInfoVerbose($className); + } + + if(!is_array($moduleInfo)) $moduleInfo = array(); + + $info = array( + 'name' => str_replace('ImageSizerEngine', '', $moduleName), + 'title' => isset($moduleInfo['title']) ? $moduleInfo['title'] : '', + 'class' => $moduleName, + 'summary' => isset($moduleInfo['summary']) ? $moduleInfo['summary'] : '', + 'author' => isset($moduleInfo['author']) ? $moduleInfo['author'] : '', + 'moduleVersion' => isset($moduleInfo['version']) ? $moduleInfo['version'] : '', + 'libraryVersion' => $this->getLibraryVersion(), + 'priority' => $this->enginePriority, + 'sources' => $formats['source'], + 'targets' => $formats['target'], + 'quality' => $this->quality, + 'sharpening' => $this->sharpening, + ); + + return $info; + } + /************************************************************************************************* * COMMON IMPLEMENTATION METHODS * @@ -1983,6 +2041,17 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable return $this->moduleConfigData; } + /** + * Get library version string + * + * @return string Returns version string or blank string if not applicable/available + * @since 3.0.138 + * + */ + public function getLibraryVersion() { + return ''; + } + /** * Module configuration * diff --git a/wire/core/ImageSizerEngineGD.php b/wire/core/ImageSizerEngineGD.php index 65bb9cb7..a2d0f416 100755 --- a/wire/core/ImageSizerEngineGD.php +++ b/wire/core/ImageSizerEngineGD.php @@ -18,6 +18,15 @@ */ class ImageSizerEngineGD extends ImageSizerEngine { + public static function getModuleInfo() { + return array( + 'title' => 'GD Image Sizer', + 'version' => 1, + 'summary' => "Uses PHP’s built-in GD library to resize images.", + 'author' => 'Horst Nogajski', + ); + } + /** * @var string * @@ -54,6 +63,30 @@ class ImageSizerEngineGD extends ImageSizerEngine { return array('JPG', 'JPEG', 'PNG', 'GIF'); } + /** + * Get an array of image file extensions this ImageSizerModule can create + * + * @return array of uppercase file extensions, i.e. ['PNG', 'JPG'] + * + */ + protected function validTargetImageFormats() { + $formats = $this->validSourceImageFormats(); + if($this->supported('webp')) $formats[] = 'WEBP'; + return $formats; + } + + /** + * Get library version string + * + * @return string Returns version string or blank string if not applicable/available + * @since 3.0.138 + * + */ + public function getLibraryVersion() { + $gd = gd_info(); + return isset($gd['GD Version']) ? $gd['GD Version'] : ''; + } + /** * Return whether or not GD can proceed - Is the current image(sub)format supported? * diff --git a/wire/core/Pageimages.php b/wire/core/Pageimages.php index e45bcbde..05fc2f49 100644 --- a/wire/core/Pageimages.php +++ b/wire/core/Pageimages.php @@ -73,7 +73,7 @@ class Pageimages extends Pagefiles { * Does this field have the given file name? If so, return it, if not return null. * * @param string $name Basename is assumed - * @return null|Pagefile Returns Pagefile object if found, null if not + * @return null|Pagefile|Pageimage Returns Pagefile object if found, null if not * */ public function getFile($name) { diff --git a/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module b/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module index 7143f3da..b6dec7fd 100755 --- a/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module +++ b/wire/modules/Image/ImageSizerEngineAnimatedGif/ImageSizerEngineAnimatedGif.module @@ -61,6 +61,18 @@ class ImageSizerEngineAnimatedGif extends ImageSizerEngine { protected function validTargetImageFormats() { return $this->validSourceImageFormats(); } + + /** + * Get library version string + * + * @return string Returns version string or blank string if not applicable/available + * @since 3.0.138 + * + */ + public function getLibraryVersion() { + $gd = gd_info(); + return isset($gd['GD Version']) ? $gd['GD Version'] : ''; + } /** * Is GD supported? diff --git a/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module b/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module index f8112073..affb9c13 100755 --- a/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module +++ b/wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module @@ -135,6 +135,18 @@ class ImageSizerEngineIMagick extends ImageSizerEngine { if($this->supportsFormat('WEBP')) $formats[] = 'WEBP'; return $formats; } + + /** + * Get library version string + * + * @return string Returns version string or blank string if not applicable/available + * @since 3.0.138 + * + */ + public function getLibraryVersion() { + $a = \Imagick::getVersion(); + return "$a[versionString] n=$a[versionNumber]"; + } /** * Is the given image format supported by this IMagick for source and target? (RJC)