"Fossies" - the Fresh Open Source Software Archive

Member "grav/vendor/gregwar/image/Gregwar/Image/Image.php" (1 Sep 2020, 19447 Bytes) of package /linux/www/grav-v1.6.27.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Image.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 
    3 namespace Gregwar\Image;
    4 
    5 use Gregwar\Cache\CacheInterface;
    6 use Gregwar\Image\Adapter\AdapterInterface;
    7 use Gregwar\Image\Exceptions\GenerationError;
    8 
    9 /**
   10  * Images handling class.
   11  *
   12  * @author Gregwar <g.passault@gmail.com>
   13  *
   14  * @method Image saveGif($file)
   15  * @method Image savePng($file)
   16  * @method Image saveJpeg($file, $quality)
   17  * @method Image resize($width = null, $height = null, $background = 'transparent', $force = false, $rescale = false, $crop = false)
   18  * @method Image forceResize($width = null, $height = null, $background = 'transparent')
   19  * @method Image scaleResize($width = null, $height = null, $background = 'transparent', $crop = false)
   20  * @method Image cropResize($width = null, $height = null, $background=0xffffff)
   21  * @method Image scale($width = null, $height = null, $background=0xffffff, $crop = false)
   22  * @method Image ($width = null, $height = null, $background = 0xffffff, $force = false, $rescale = false, $crop = false)
   23  * @method Image crop($x, $y, $width, $height)
   24  * @method Image enableProgressive()
   25  * @method Image force($width = null, $height = null, $background = 0xffffff)
   26  * @method Image zoomCrop($width, $height, $background = 0xffffff, $xPos, $yPos)
   27  * @method Image fillBackground($background = 0xffffff)
   28  * @method Image negate()
   29  * @method Image brightness($brightness)
   30  * @method Image contrast($contrast)
   31  * @method Image grayscale()
   32  * @method Image emboss()
   33  * @method Image smooth($p)
   34  * @method Image sharp()
   35  * @method Image edge()
   36  * @method Image colorize($red, $green, $blue)
   37  * @method Image sepia()
   38  * @method Image merge(Image $other, $x = 0, $y = 0, $width = null, $height = null)
   39  * @method Image rotate($angle, $background = 0xffffff)
   40  * @method Image fill($color = 0xffffff, $x = 0, $y = 0)
   41  * @method Image write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $align = 'left')
   42  * @method Image rectangle($x1, $y1, $x2, $y2, $color, $filled = false)
   43  * @method Image roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled = false)
   44  * @method Image line($x1, $y1, $x2, $y2, $color = 0x000000)
   45  * @method Image ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false)
   46  * @method Image circle($cx, $cy, $r, $color = 0x000000, $filled = false)
   47  * @method Image polygon(array $points, $color, $filled = false)
   48  * @method Image flip($flipVertical, $flipHorizontal)
   49  */
   50 class Image
   51 {
   52     /**
   53      * Directory to use for file caching.
   54      */
   55     protected $cacheDir = 'cache/images';
   56 
   57     /**
   58      * Directory cache mode.
   59      */
   60     protected $cacheMode = null;
   61 
   62     /**
   63      * Internal adapter.
   64      *
   65      * @var AdapterInterface
   66      */
   67     protected $adapter = null;
   68 
   69     /**
   70      * Pretty name for the image.
   71      */
   72     protected $prettyName = '';
   73     protected $prettyPrefix;
   74 
   75     /**
   76      * Transformations hash.
   77      */
   78     protected $hash = null;
   79 
   80     /**
   81      * The image source.
   82      */
   83     protected $source = null;
   84 
   85     /**
   86      * Force image caching, even if there is no operation applied.
   87      */
   88     protected $forceCache = true;
   89 
   90     /**
   91      * Supported types.
   92      */
   93     public static $types = array(
   94         'jpg'   => 'jpeg',
   95         'jpeg'  => 'jpeg',
   96         'webp'  => 'webp',
   97         'png'   => 'png',
   98         'gif'   => 'gif',
   99     );
  100 
  101     /**
  102      * Fallback image.
  103      */
  104     protected $fallback;
  105 
  106     /**
  107      * Use fallback image.
  108      */
  109     protected $useFallbackImage = true;
  110 
  111     /**
  112      * Cache system.
  113      *
  114      * @var \Gregwar\Cache\CacheInterface
  115      */
  116     protected $cache;
  117 
  118     /**
  119      * Get the cache system.
  120      *
  121      * @return \Gregwar\Cache\CacheInterface
  122      */
  123     public function getCacheSystem()
  124     {
  125         if (is_null($this->cache)) {
  126             $this->cache = new \Gregwar\Cache\Cache();
  127             $this->cache->setCacheDirectory($this->cacheDir);
  128         }
  129 
  130         return $this->cache;
  131     }
  132 
  133     /**
  134      * Set the cache system.
  135      *
  136      * @param \Gregwar\Cache\CacheInterface $cache
  137      */
  138     public function setCacheSystem(CacheInterface $cache)
  139     {
  140         $this->cache = $cache;
  141     }
  142 
  143     /**
  144      * Change the caching directory.
  145      */
  146     public function setCacheDir($cacheDir)
  147     {
  148         $this->getCacheSystem()->setCacheDirectory($cacheDir);
  149 
  150         return $this;
  151     }
  152 
  153     /**
  154      * @param int $dirMode
  155      */
  156     public function setCacheDirMode($dirMode)
  157     {
  158         $this->cache->setDirectoryMode($dirMode);
  159     }
  160 
  161     /**
  162      * Enable or disable to force cache even if the file is unchanged.
  163      */
  164     public function setForceCache($forceCache = true)
  165     {
  166         $this->forceCache = $forceCache;
  167 
  168         return $this;
  169     }
  170 
  171     /**
  172      * The actual cache dir.
  173      */
  174     public function setActualCacheDir($actualCacheDir)
  175     {
  176         $this->getCacheSystem()->setActualCacheDirectory($actualCacheDir);
  177 
  178         return $this;
  179     }
  180 
  181     /**
  182      * Sets the pretty name of the image.
  183      */
  184     public function setPrettyName($name, $prefix = true)
  185     {
  186         if (empty($name)) {
  187             return $this;
  188         }
  189 
  190         $this->prettyName = $this->urlize($name);
  191         $this->prettyPrefix = $prefix;
  192 
  193         return $this;
  194     }
  195 
  196     /**
  197      * Urlizes the prettyName.
  198      */
  199     protected function urlize($name)
  200     {
  201         $transliterator = '\Behat\Transliterator\Transliterator';
  202 
  203         if (class_exists($transliterator)) {
  204             $name = $transliterator::transliterate($name);
  205             $name = $transliterator::urlize($name);
  206         } else {
  207             $name = strtolower($name);
  208             $name = str_replace(' ', '-', $name);
  209             $name = preg_replace('/([^a-z0-9\-]+)/m', '', $name);
  210         }
  211 
  212         return $name;
  213     }
  214 
  215     /**
  216      * Operations array.
  217      */
  218     protected $operations = array();
  219 
  220     public function __construct($originalFile = null, $width = null, $height = null)
  221     {
  222         $this->setFallback(null);
  223 
  224         if ($originalFile) {
  225             $this->source = new Source\File($originalFile);
  226         } else {
  227             $this->source = new Source\Create($width, $height);
  228         }
  229     }
  230 
  231     /**
  232      * Sets the image data.
  233      */
  234     public function setData($data)
  235     {
  236         $this->source = new Source\Data($data);
  237     }
  238 
  239     /**
  240      * Sets the resource.
  241      */
  242     public function setResource($resource)
  243     {
  244         $this->source = new Source\Resource($resource);
  245     }
  246 
  247     /**
  248      * Use the fallback image or not.
  249      */
  250     public function useFallback($useFallbackImage = true)
  251     {
  252         $this->useFallbackImage = $useFallbackImage;
  253 
  254         return $this;
  255     }
  256 
  257     /**
  258      * Sets the fallback image to use.
  259      */
  260     public function setFallback($fallback = null)
  261     {
  262         if ($fallback === null) {
  263             $this->fallback = __DIR__.'/images/error.jpg';
  264         } else {
  265             $this->fallback = $fallback;
  266         }
  267 
  268         return $this;
  269     }
  270 
  271     /**
  272      * Gets the fallack image path.
  273      */
  274     public function getFallback()
  275     {
  276         return $this->fallback;
  277     }
  278 
  279     /**
  280      * Gets the fallback into the cache dir.
  281      */
  282     public function getCacheFallback()
  283     {
  284         $fallback = $this->fallback;
  285 
  286         return $this->getCacheSystem()->getOrCreateFile('fallback.jpg', array(), function ($target) use ($fallback) {
  287             copy($fallback, $target);
  288         });
  289     }
  290 
  291     /**
  292      * @return AdapterInterface
  293      */
  294     public function getAdapter()
  295     {
  296         if (null === $this->adapter) {
  297             // Defaults to GD
  298             $this->setAdapter('gd');
  299         }
  300 
  301         return $this->adapter;
  302     }
  303 
  304     public function setAdapter($adapter)
  305     {
  306         if ($adapter instanceof Adapter\Adapter) {
  307             $this->adapter = $adapter;
  308         } else {
  309             if (is_string($adapter)) {
  310                 $adapter = strtolower($adapter);
  311 
  312                 switch ($adapter) {
  313                 case 'gd':
  314                     $this->adapter = new Adapter\GD();
  315                     break;
  316                 case 'imagemagick':
  317                 case 'imagick':
  318                     $this->adapter = new Adapter\Imagick();
  319                     break;
  320                 default:
  321                     throw new \Exception('Unknown adapter: '.$adapter);
  322                     break;
  323                 }
  324             } else {
  325                 throw new \Exception('Unable to load the given adapter (not string or Adapter)');
  326             }
  327         }
  328 
  329         $this->adapter->setSource($this->source);
  330     }
  331 
  332     /**
  333      * Get the file path.
  334      *
  335      * @return mixed a string with the filen name, null if the image
  336      *               does not depends on a file
  337      */
  338     public function getFilePath()
  339     {
  340         if ($this->source instanceof Source\File) {
  341             return $this->source->getFile();
  342         } else {
  343             return;
  344         }
  345     }
  346 
  347     /**
  348      * Defines the file only after instantiation.
  349      *
  350      * @param string $originalFile the file path
  351      */
  352     public function fromFile($originalFile)
  353     {
  354         $this->source = new Source\File($originalFile);
  355 
  356         return $this;
  357     }
  358 
  359     /**
  360      * Tells if the image is correct.
  361      */
  362     public function correct()
  363     {
  364         return $this->source->correct();
  365     }
  366 
  367     /**
  368      * Guess the file type.
  369      */
  370     public function guessType()
  371     {
  372         return $this->source->guessType();
  373     }
  374 
  375     /**
  376      * Adds an operation.
  377      */
  378     protected function addOperation($method, $args)
  379     {
  380         $this->operations[] = array($method, $args);
  381     }
  382 
  383     /**
  384      * Generic function.
  385      */
  386     public function __call($methodName, $args)
  387     {
  388         $adapter = $this->getAdapter();
  389         $reflection = new \ReflectionClass(get_class($adapter));
  390 
  391         if ($reflection->hasMethod($methodName)) {
  392             $method = $reflection->getMethod($methodName);
  393 
  394             if ($method->getNumberOfRequiredParameters() > count($args)) {
  395                 throw new \InvalidArgumentException('Not enough arguments given for '.$methodName);
  396             }
  397 
  398             $this->addOperation($methodName, $args);
  399 
  400             return $this;
  401         }
  402 
  403         throw new \BadFunctionCallException('Invalid method: '.$methodName);
  404     }
  405 
  406     /**
  407      * Serialization of operations.
  408      */
  409     public function serializeOperations()
  410     {
  411         $datas = array();
  412 
  413         foreach ($this->operations as $operation) {
  414             $method = $operation[0];
  415             $args = $operation[1];
  416 
  417             foreach ($args as &$arg) {
  418                 if ($arg instanceof self) {
  419                     $arg = $arg->getHash();
  420                 }
  421             }
  422 
  423             $datas[] = array($method, $args);
  424         }
  425 
  426         return serialize($datas);
  427     }
  428 
  429     /**
  430      * Generates the hash.
  431      */
  432     public function generateHash($type = 'guess', $quality = 80)
  433     {
  434         $inputInfos = $this->source->getInfos();
  435 
  436         $datas = array(
  437             $inputInfos,
  438             $this->serializeOperations(),
  439             $type,
  440             $quality,
  441         );
  442 
  443         $this->hash = sha1(serialize($datas));
  444     }
  445 
  446     /**
  447      * Gets the hash.
  448      */
  449     public function getHash($type = 'guess', $quality = 80)
  450     {
  451         if (null === $this->hash) {
  452             $this->generateHash($type, $quality);
  453         }
  454 
  455         return $this->hash;
  456     }
  457 
  458     /**
  459      * Gets the cache file name and generate it if it does not exists.
  460      * Note that if it exists, all the image computation process will
  461      * not be done.
  462      *
  463      * @param string $type    the image type
  464      * @param int    $quality the quality (for JPEG)
  465      */
  466     public function cacheFile($type = 'jpg', $quality = 80, $actual = false)
  467     {
  468         if ($type == 'guess') {
  469             $type = $this->guessType();
  470         }
  471 
  472         if (!count($this->operations) && $type == $this->guessType() && !$this->forceCache) {
  473             return $this->getFilename($this->getFilePath());
  474         }
  475 
  476         // Computes the hash
  477         $this->hash = $this->getHash($type, $quality);
  478 
  479         // Generates the cache file
  480         $cacheFile = '';
  481 
  482         if (!$this->prettyName || $this->prettyPrefix) {
  483             $cacheFile .= $this->hash;
  484         }
  485 
  486         if ($this->prettyPrefix) {
  487             $cacheFile .= '-';
  488         }
  489 
  490         if ($this->prettyName) {
  491             $cacheFile .= $this->prettyName;
  492         }
  493 
  494         $cacheFile .= '.'.$type;
  495 
  496         // If the files does not exists, save it
  497         $image = $this;
  498 
  499         // Target file should be younger than all the current image
  500         // dependencies
  501         $conditions = array(
  502             'younger-than' => $this->getDependencies(),
  503         );
  504 
  505         // The generating function
  506         $generate = function ($target) use ($image, $type, $quality) {
  507             $result = $image->save($target, $type, $quality);
  508 
  509             if ($result != $target) {
  510                 throw new GenerationError($result);
  511             }
  512         };
  513 
  514         // Asking the cache for the cacheFile
  515         try {
  516             $file = $this->getCacheSystem()->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
  517         } catch (GenerationError $e) {
  518             $file = $e->getNewFile();
  519         }
  520 
  521         // Nulling the resource
  522         $this->getAdapter()->setSource(new Source\File($file));
  523         $this->getAdapter()->deinit();
  524 
  525         if ($actual) {
  526             return $file;
  527         } else {
  528             return $this->getFilename($file);
  529         }
  530     }
  531 
  532     /**
  533      * Get cache data (to render the image).
  534      *
  535      * @param string $type    the image type
  536      * @param int    $quality the quality (for JPEG)
  537      */
  538     public function cacheData($type = 'jpg', $quality = 80)
  539     {
  540         return file_get_contents($this->cacheFile($type, $quality));
  541     }
  542 
  543     /**
  544      * Hook to helps to extends and enhance this class.
  545      */
  546     protected function getFilename($filename)
  547     {
  548         return $filename;
  549     }
  550 
  551     /**
  552      * Generates and output a jpeg cached file.
  553      */
  554     public function jpeg($quality = 80)
  555     {
  556         return $this->cacheFile('jpg', $quality);
  557     }
  558 
  559     /**
  560      * Generates and output a gif cached file.
  561      */
  562     public function gif()
  563     {
  564         return $this->cacheFile('gif');
  565     }
  566 
  567     /**
  568      * Generates and output a png cached file.
  569      */
  570     public function png()
  571     {
  572         return $this->cacheFile('png');
  573     }
  574 
  575     /**
  576      * Generates and output a png cached file.
  577      */
  578     public function webp()
  579     {
  580         return $this->cacheFile('webp');
  581     }
  582 
  583     /**
  584      * Generates and output an image using the same type as input.
  585      */
  586     public function guess($quality = 80)
  587     {
  588         return $this->cacheFile('guess', $quality);
  589     }
  590 
  591     /**
  592      * Get all the files that this image depends on.
  593      *
  594      * @return string[] this is an array of strings containing all the files that the
  595      *                  current Image depends on
  596      */
  597     public function getDependencies()
  598     {
  599         $dependencies = array();
  600 
  601         $file = $this->getFilePath();
  602         if ($file) {
  603             $dependencies[] = $file;
  604         }
  605 
  606         foreach ($this->operations as $operation) {
  607             foreach ($operation[1] as $argument) {
  608                 if ($argument instanceof self) {
  609                     $dependencies = array_merge($dependencies, $argument->getDependencies());
  610                 }
  611             }
  612         }
  613 
  614         return $dependencies;
  615     }
  616 
  617     /**
  618      * Applies the operations.
  619      */
  620     public function applyOperations()
  621     {
  622         // Renders the effects
  623         foreach ($this->operations as $operation) {
  624             call_user_func_array(array($this->adapter, $operation[0]), $operation[1]);
  625         }
  626     }
  627 
  628     /**
  629      * Initialize the adapter.
  630      */
  631     public function init()
  632     {
  633         $this->getAdapter()->init();
  634     }
  635 
  636     /**
  637      * Save the file to a given output.
  638      */
  639     public function save($file, $type = 'guess', $quality = 80)
  640     {
  641         if ($file) {
  642             $directory = dirname($file);
  643 
  644             if (!is_dir($directory)) {
  645                 @mkdir($directory, 0777, true);
  646             }
  647         }
  648 
  649         if (is_int($type)) {
  650             $quality = $type;
  651             $type = 'jpeg';
  652         }
  653 
  654         if ($type == 'guess') {
  655             $type = $this->guessType();
  656         }
  657 
  658         if (!isset(self::$types[$type])) {
  659             throw new \InvalidArgumentException('Given type ('.$type.') is not valid');
  660         }
  661 
  662         $type = self::$types[$type];
  663 
  664         try {
  665             $this->init();
  666             $this->applyOperations();
  667 
  668             $success = false;
  669 
  670             if (null == $file) {
  671                 ob_start();
  672             }
  673 
  674             if ($type == 'jpeg') {
  675                 $success = $this->getAdapter()->saveJpeg($file, $quality);
  676             }
  677 
  678             if ($type == 'gif') {
  679                 $success = $this->getAdapter()->saveGif($file);
  680             }
  681 
  682             if ($type == 'png') {
  683                 $success = $this->getAdapter()->savePng($file);
  684             }
  685 
  686             if ($type == 'webp') {
  687                 $success = $this->getAdapter()->saveWebP($file, $quality);
  688             }
  689 
  690             if (!$success) {
  691                 return false;
  692             }
  693 
  694             return null === $file ? ob_get_clean() : $file;
  695         } catch (\Exception $e) {
  696             if ($this->useFallbackImage) {
  697                 return null === $file ? file_get_contents($this->fallback) : $this->getCacheFallback();
  698             } else {
  699                 throw $e;
  700             }
  701         }
  702     }
  703 
  704     /**
  705      * Get the contents of the image.
  706      */
  707     public function get($type = 'guess', $quality = 80)
  708     {
  709         return $this->save(null, $type, $quality);
  710     }
  711 
  712     /* Image API */
  713 
  714     /**
  715      * Image width.
  716      */
  717     public function width()
  718     {
  719         return $this->getAdapter()->width();
  720     }
  721 
  722     /**
  723      * Image height.
  724      */
  725     public function height()
  726     {
  727         return $this->getAdapter()->height();
  728     }
  729 
  730     /**
  731      * Tostring defaults to jpeg.
  732      */
  733     public function __toString()
  734     {
  735         return $this->guess();
  736     }
  737 
  738     /**
  739      * Returning basic html code for this image.
  740      */
  741     public function html($title = '', $type = 'jpg', $quality = 80)
  742     {
  743         return '<img title="'.$title.'" src="'.$this->cacheFile($type, $quality).'" />';
  744     }
  745 
  746     /**
  747      * Returns the Base64 inlinable representation.
  748      */
  749     public function inline($type = 'jpg', $quality = 80)
  750     {
  751         $mime = $type;
  752         if ($mime == 'jpg') {
  753             $mime = 'jpeg';
  754         }
  755 
  756         return 'data:image/'.$mime.';base64,'.base64_encode(file_get_contents($this->cacheFile($type, $quality, true)));
  757     }
  758 
  759     /**
  760      * Creates an instance, usefull for one-line chaining.
  761      */
  762     public static function open($file = '')
  763     {
  764         return new static($file);
  765     }
  766 
  767     /**
  768      * Creates an instance of a new resource.
  769      */
  770     public static function create($width, $height)
  771     {
  772         return new static(null, $width, $height);
  773     }
  774 
  775     /**
  776      * Creates an instance of image from its data.
  777      */
  778     public static function fromData($data)
  779     {
  780         $image = new static();
  781         $image->setData($data);
  782 
  783         return $image;
  784     }
  785 
  786     /**
  787      * Creates an instance of image from resource.
  788      */
  789     public static function fromResource($resource)
  790     {
  791         $image = new static();
  792         $image->setResource($resource);
  793 
  794         return $image;
  795     }
  796 }