Add X13 WebP module for image conversion to next-generation formats
- Implemented the X13Webp class with core functionalities for converting images to WebP format. - Added support for different PHP versions and defined constants for versioning. - Included translation strings for various user interface elements and messages. - Created XML file for module versioning.
This commit is contained in:
112
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/ConverterFactory.php
vendored
Normal file
112
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/ConverterFactory.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
|
||||
/**
|
||||
* Make converters from their ids.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ConverterFactory
|
||||
{
|
||||
/**
|
||||
* Get classname of a converter (by id)
|
||||
*
|
||||
* @param string $converterId Id of converter (ie "cwebp")
|
||||
*
|
||||
* @throws ConverterNotFoundException If there is no converter with that id.
|
||||
* @return string Fully qualified class name of converter
|
||||
*/
|
||||
public static function converterIdToClassname($converterId)
|
||||
{
|
||||
switch ($converterId) {
|
||||
case 'ffmpeg':
|
||||
$classNameShort = 'FFMpeg';
|
||||
break;
|
||||
case 'imagickbinary':
|
||||
$classNameShort = 'ImagickBinary';
|
||||
break;
|
||||
case 'imagemagick':
|
||||
$classNameShort = 'ImageMagick';
|
||||
break;
|
||||
case 'gmagickbinary':
|
||||
$classNameShort = 'GmagickBinary';
|
||||
break;
|
||||
case 'graphicsmagick':
|
||||
$classNameShort = 'GraphicsMagick';
|
||||
break;
|
||||
default:
|
||||
$classNameShort = ucfirst($converterId);
|
||||
}
|
||||
$className = 'WebPConvert\\Convert\\Converters\\' . $classNameShort;
|
||||
if (is_callable([$className, 'convert'])) {
|
||||
return $className;
|
||||
} else {
|
||||
throw new ConverterNotFoundException('There is no converter with id:' . $converterId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a converter instance by class name.
|
||||
*
|
||||
* @param string $converterClassName Fully qualified class name
|
||||
* @param string $source The path to the file to convert
|
||||
* @param string $destination The path to save the converted file to
|
||||
* @param array $options (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws ConverterNotFoundException If the specified converter class isn't found
|
||||
* @return AbstractConverter An instance of the specified converter
|
||||
*/
|
||||
public static function makeConverterFromClassname(
|
||||
$converterClassName,
|
||||
$source,
|
||||
$destination,
|
||||
$options = [],
|
||||
$logger = null
|
||||
) {
|
||||
if (!is_callable([$converterClassName, 'convert'])) {
|
||||
throw new ConverterNotFoundException(
|
||||
'There is no converter with class name:' . $converterClassName . ' (or it is not a converter)'
|
||||
);
|
||||
}
|
||||
//$converter = new $converterClassName($source, $destination, $options, $logger);
|
||||
|
||||
return call_user_func(
|
||||
[$converterClassName, 'createInstance'],
|
||||
$source,
|
||||
$destination,
|
||||
$options,
|
||||
$logger
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a converter instance by either id or class name.
|
||||
*
|
||||
* @param string $converterIdOrClassName Either a converter ID or a fully qualified class name
|
||||
* @param string $source The path to the file to convert
|
||||
* @param string $destination The path to save the converted file to
|
||||
* @param array $options (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws ConverterNotFoundException If the specified converter class isn't found
|
||||
* @return AbstractConverter An instance of the specified converter
|
||||
*/
|
||||
public static function makeConverter($converterIdOrClassName, $source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
// We take it that all lowercase means it is an id rather than a class name
|
||||
if (strtolower($converterIdOrClassName) == $converterIdOrClassName) {
|
||||
$converterClassName = self::converterIdToClassname($converterIdOrClassName);
|
||||
} else {
|
||||
$converterClassName = $converterIdOrClassName;
|
||||
}
|
||||
|
||||
return self::makeConverterFromClassname($converterClassName, $source, $destination, $options, $logger);
|
||||
}
|
||||
}
|
||||
387
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/AbstractConverter.php
vendored
Normal file
387
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/AbstractConverter.php
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
// TODO:
|
||||
// Read this: https://sourcemaking.com/design_patterns/strategy
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Helpers\MimeType;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\AutoQualityTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\DestinationPreparationTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\LoggerTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\OptionsTrait;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
use WebPConvert\Loggers\BaseLogger;
|
||||
|
||||
/**
|
||||
* Base for all converter classes.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
abstract class AbstractConverter
|
||||
{
|
||||
use AutoQualityTrait;
|
||||
use OptionsTrait;
|
||||
use WarningLoggerTrait;
|
||||
use DestinationPreparationTrait;
|
||||
use LoggerTrait;
|
||||
|
||||
/**
|
||||
* The actual conversion is be done by a concrete converter extending this class.
|
||||
*
|
||||
* At the stage this method is called, the abstract converter has taken preparational steps.
|
||||
* - It has created the destination folder (if neccesary)
|
||||
* - It has checked the input (valid mime type)
|
||||
* - It has set up an error handler, mostly in order to catch and log warnings during the doConvert fase
|
||||
*
|
||||
* Note: This method is not meant to be called from the outside. Use the static *convert* method for converting
|
||||
* or, if you wish, create an instance with ::createInstance() and then call ::doConvert()
|
||||
*
|
||||
* @throws ConversionFailedException in case conversion failed in an antipiciated way (or subclass)
|
||||
* @throws \Exception in case conversion failed in an unantipiciated way
|
||||
*/
|
||||
abstract protected function doActualConvert();
|
||||
|
||||
/**
|
||||
* Whether or not the converter supports lossless encoding (even for jpegs)
|
||||
*
|
||||
* PS: Converters that supports lossless encoding all use the EncodingAutoTrait, which
|
||||
* overrides this function.
|
||||
*
|
||||
* @return boolean Whether the converter supports lossless encoding (even for jpegs).
|
||||
*/
|
||||
public function supportsLossless()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string The filename of the image to convert (complete path) */
|
||||
protected $source;
|
||||
|
||||
/** @var string Where to save the webp (complete path) */
|
||||
protected $destination;
|
||||
|
||||
/**
|
||||
* Check basis operationality
|
||||
*
|
||||
* Converters may override this method for the purpose of performing basic operationaly checks. It is for
|
||||
* running general operation checks for a conversion method.
|
||||
* If some requirement is not met, it should throw a ConverterNotOperationalException (or subtype)
|
||||
*
|
||||
* The method is called internally right before calling doActualConvert() method.
|
||||
* - It SHOULD take options into account when relevant. For example, a missing api key for a
|
||||
* cloud converter should be detected here
|
||||
* - It should NOT take the actual filename into consideration, as the purpose is *general*
|
||||
* For that pupose, converters should override checkConvertability
|
||||
* Also note that doConvert method is allowed to throw ConverterNotOperationalException too.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Converters may override this for the purpose of performing checks on the concrete file.
|
||||
*
|
||||
* This can for example be used for rejecting big uploads in cloud converters or rejecting unsupported
|
||||
* image types.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for conversion
|
||||
* @param BaseLogger $logger (optional)
|
||||
*/
|
||||
final public function __construct($source = '', $destination = '', $options = [], $logger = null)
|
||||
{
|
||||
if ($source == '') {
|
||||
return;
|
||||
}
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
|
||||
$this->source = $source;
|
||||
$this->destination = $destination;
|
||||
|
||||
$this->setLogger($logger);
|
||||
$this->setProvidedOptions($options);
|
||||
|
||||
if (!isset($this->options['_skip_input_check'])) {
|
||||
$this->logLn('WebP Convert 2.9.0 ignited', 'bold');
|
||||
$this->logLn('PHP version: ' . phpversion());
|
||||
if (isset($_SERVER['SERVER_SOFTWARE'])) {
|
||||
$this->logLn('Server software: ' . $_SERVER['SERVER_SOFTWARE']);
|
||||
}
|
||||
$this->logLn('');
|
||||
if (isset($this->options['log-call-arguments']) && $this->options['log-call-arguments']) {
|
||||
$this->logLn('source: ' . $this->source);
|
||||
$this->logLn('destination: ' . $this->destination);
|
||||
$this->logLn('');
|
||||
}
|
||||
|
||||
$this->logLn(self::getConverterDisplayName() . ' converter ignited', 'bold');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source.
|
||||
*
|
||||
* @return string The source.
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination.
|
||||
*
|
||||
* @return string The destination.
|
||||
*/
|
||||
public function getDestination()
|
||||
{
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set destination.
|
||||
*
|
||||
* @param string $destination path to destination
|
||||
* @return void
|
||||
*/
|
||||
public function setDestination($destination)
|
||||
{
|
||||
$this->destination = $destination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get converter name for display (defaults to the class name (short)).
|
||||
*
|
||||
* Converters can override this.
|
||||
*
|
||||
* @return string A display name, ie "Gd"
|
||||
*/
|
||||
protected static function getConverterDisplayName()
|
||||
{
|
||||
// https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name/25308464
|
||||
return substr(strrchr('\\' . static::class, '\\'), 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get converter id (defaults to the class name lowercased)
|
||||
*
|
||||
* Converters can override this.
|
||||
*
|
||||
* @return string A display name, ie "Gd"
|
||||
*/
|
||||
protected static function getConverterId()
|
||||
{
|
||||
return strtolower(self::getConverterDisplayName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance of this class
|
||||
*
|
||||
* @param string $source The path to the file to convert
|
||||
* @param string $destination The path to save the converted file to
|
||||
* @param array $options (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createInstance($source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
return new static($source, $destination, $options, $logger);
|
||||
}
|
||||
|
||||
protected function logReduction($source, $destination)
|
||||
{
|
||||
$sourceSize = filesize($source);
|
||||
$destSize = filesize($destination);
|
||||
$this->log(round(($sourceSize - $destSize) / $sourceSize * 100) . '% ');
|
||||
if ($sourceSize < 10000) {
|
||||
$this->logLn('(went from ' . strval($sourceSize) . ' bytes to ' . strval($destSize) . ' bytes)');
|
||||
} else {
|
||||
$this->logLn('(went from ' . round($sourceSize / 1024) . ' kb to ' . round($destSize / 1024) . ' kb)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run conversion.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function doConvertImplementation()
|
||||
{
|
||||
$beginTime = microtime(true);
|
||||
|
||||
$this->activateWarningLogger();
|
||||
|
||||
$this->checkOptions();
|
||||
|
||||
// Prepare destination folder
|
||||
$this->createWritableDestinationFolder();
|
||||
$this->removeExistingDestinationIfExists();
|
||||
|
||||
if (!isset($this->options['_skip_input_check'])) {
|
||||
// Check that a file can be written to destination
|
||||
$this->checkDestinationWritable();
|
||||
}
|
||||
|
||||
$this->checkOperationality();
|
||||
$this->checkConvertability();
|
||||
|
||||
if ($this->options['log-call-arguments']) {
|
||||
$this->logOptions();
|
||||
$this->logLn('');
|
||||
}
|
||||
|
||||
$this->runActualConvert();
|
||||
|
||||
$source = $this->source;
|
||||
$destination = $this->destination;
|
||||
|
||||
if (!@file_exists($destination)) {
|
||||
throw new ConversionFailedException('Destination file is not there: ' . $destination);
|
||||
} elseif (@filesize($destination) === 0) {
|
||||
unlink($destination);
|
||||
throw new ConversionFailedException('Destination file was completely empty');
|
||||
} else {
|
||||
if (!isset($this->options['_suppress_success_message'])) {
|
||||
$this->ln();
|
||||
$this->log('Converted image in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
|
||||
|
||||
$sourceSize = @filesize($source);
|
||||
if ($sourceSize !== false) {
|
||||
$this->log(', reducing file size with ');
|
||||
$this->logReduction($source, $destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->deactivateWarningLogger();
|
||||
}
|
||||
|
||||
//private function logEx
|
||||
/**
|
||||
* Start conversion.
|
||||
*
|
||||
* Usually it would be more convenience to call the static convert method, but alternatively you can call
|
||||
* call ::createInstance to get an instance and then ::doConvert().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function doConvert()
|
||||
{
|
||||
try {
|
||||
//trigger_error('hello', E_USER_ERROR);
|
||||
$this->doConvertImplementation();
|
||||
} catch (WebPConvertException $e) {
|
||||
$this->logLn('');
|
||||
/*
|
||||
if (isset($e->description) && ($e->description != '')) {
|
||||
$this->log('Error: ' . $e->description . '. ', 'bold');
|
||||
} else {
|
||||
$this->log('Error: ', 'bold');
|
||||
}
|
||||
*/
|
||||
$this->log('Error: ', 'bold');
|
||||
$this->logLn($e->getMessage(), 'bold');
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
$className = get_class($e);
|
||||
$classNameParts = explode("\\", $className);
|
||||
$shortClassName = array_pop($classNameParts);
|
||||
|
||||
$this->logLn('');
|
||||
$this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
|
||||
$this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
|
||||
|
||||
$this->logLn('Trace:');
|
||||
foreach ($e->getTrace() as $trace) {
|
||||
//$this->logLn(print_r($trace, true));
|
||||
if (isset($trace['file']) && isset($trace['line'])) {
|
||||
$this->logLn(
|
||||
$trace['file'] . ':' . $trace['line']
|
||||
);
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$className = get_class($e);
|
||||
$classNameParts = explode("\\", $className);
|
||||
$shortClassName = array_pop($classNameParts);
|
||||
|
||||
$this->logLn('');
|
||||
$this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
|
||||
$this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actual conversion (after setup and checks)
|
||||
* Simply calls the doActualConvert() of the actual converter.
|
||||
* However, in the EncodingAutoTrait, this method is overridden to make two conversions
|
||||
* and select the smallest.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function runActualConvert()
|
||||
{
|
||||
$this->doActualConvert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an image to webp.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for conversion
|
||||
* @param BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws ConversionFailedException in case conversion fails in an antipiciated way
|
||||
* @throws \Exception in case conversion fails in an unantipiciated way
|
||||
* @return void
|
||||
*/
|
||||
public static function convert($source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
$c = self::createInstance($source, $destination, $options, $logger);
|
||||
$c->doConvert();
|
||||
//echo $instance->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mime type for image (best guess).
|
||||
*
|
||||
* It falls back to using file extension. If that fails too, false is returned
|
||||
*
|
||||
* PS: Is it a security risk to fall back on file extension?
|
||||
* - By setting file extension to "jpg", one can lure our library into trying to convert a file, which isn't a jpg.
|
||||
* hmm, seems very unlikely, though not unthinkable that one of the converters could be exploited
|
||||
*
|
||||
* @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
|
||||
* false (if it is not an image type that the server knowns about)
|
||||
* or null (if nothing can be determined)
|
||||
*/
|
||||
public function getMimeTypeOfSource()
|
||||
{
|
||||
return MimeType::getMimeTypeDetectionResult($this->source);
|
||||
}
|
||||
}
|
||||
186
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/AutoQualityTrait.php
vendored
Normal file
186
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/AutoQualityTrait.php
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
use WebPConvert\Convert\Helpers\JpegQualityDetector;
|
||||
|
||||
/**
|
||||
* Trait for handling the "quality:auto" option.
|
||||
*
|
||||
* This trait is only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning auto quality.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait AutoQualityTrait
|
||||
{
|
||||
|
||||
abstract public function logLn($msg, $style = '');
|
||||
abstract public function getMimeTypeOfSource();
|
||||
|
||||
/** @var boolean Whether the quality option has been processed or not */
|
||||
private $processed = false;
|
||||
|
||||
/** @var boolean Whether the quality of the source could be detected or not (set upon processing) */
|
||||
private $qualityCouldNotBeDetected = false;
|
||||
|
||||
/** @var integer The calculated quality (set upon processing - on successful detection) */
|
||||
private $calculatedQuality;
|
||||
|
||||
|
||||
/**
|
||||
* Determine if quality detection is required but failing.
|
||||
*
|
||||
* It is considered "required" when:
|
||||
* - Mime type is "image/jpeg"
|
||||
* - Quality is set to "auto"
|
||||
*
|
||||
* If quality option hasn't been proccessed yet, it is triggered.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isQualityDetectionRequiredButFailing()
|
||||
{
|
||||
$this->processQualityOptionIfNotAlready();
|
||||
return $this->qualityCouldNotBeDetected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calculated quality.
|
||||
*
|
||||
* If the "quality" option is a number, that number is returned.
|
||||
* If mime type of source is something else than "image/jpeg", the "default-quality" option is returned
|
||||
* If quality is "auto" and source is a jpeg image, it will be attempted to detect jpeg quality.
|
||||
* In case of failure, the value of the "default-quality" option is returned.
|
||||
* In case of success, the detected quality is returned, or the value of the "max-quality" if that is lower.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCalculatedQuality()
|
||||
{
|
||||
$this->processQualityOptionIfNotAlready();
|
||||
return $this->calculatedQuality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the quality option if it is not already processed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processQualityOptionIfNotAlready()
|
||||
{
|
||||
if (!$this->processed) {
|
||||
$this->processed = true;
|
||||
$this->processQualityOption();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the quality option.
|
||||
*
|
||||
* Sets the private property "calculatedQuality" according to the description for the getCalculatedQuality
|
||||
* function.
|
||||
* In case quality detection was attempted and failed, the private property "qualityCouldNotBeDetected" is set
|
||||
* to true. This is used by the "isQualityDetectionRequiredButFailing" (and documented there too).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processQualityOption()
|
||||
{
|
||||
$options = $this->options;
|
||||
$source = $this->source;
|
||||
|
||||
/*
|
||||
Mapping from old options to new options:
|
||||
quality: "auto", max-quality: 85, default-quality: 75
|
||||
becomes: quality: 85, auto-limit: true
|
||||
|
||||
quality: 80
|
||||
becomes: quality: 80, auto-limit: false
|
||||
*/
|
||||
$q = $options['quality'];
|
||||
$useDeprecatedDefaultQuality = false;
|
||||
if ($q == 'auto') {
|
||||
$q = $options['quality'] = $options['max-quality'];
|
||||
$this->logLn(
|
||||
'*Setting "quality" to "auto" is deprecated. ' .
|
||||
'Instead, set "quality" to a number (0-100) and "auto-limit" to true. '
|
||||
);
|
||||
$this->logLn(
|
||||
'*"quality" has been set to: ' . $options['max-quality'] . ' (took the value of "max-quality").*'
|
||||
);
|
||||
if (!$this->options2->getOptionById('auto-limit')->isValueExplicitlySet()) {
|
||||
$options['auto-limit'] = true;
|
||||
$this->logLn(
|
||||
'*"auto-limit" has been set to: true."*'
|
||||
);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'*PS: "auto-limit" is set to false, as it was set explicitly to false in the options."*'
|
||||
);
|
||||
}
|
||||
$useDeprecatedDefaultQuality = true;
|
||||
}
|
||||
|
||||
if ($options['auto-limit']) {
|
||||
if (($this->/** @scrutinizer ignore-call */getMimeTypeOfSource() == 'image/jpeg')) {
|
||||
$this->logLn('Running auto-limit');
|
||||
$this->logLn(
|
||||
'Quality setting: ' . $q . '. '
|
||||
);
|
||||
$q = JpegQualityDetector::detectQualityOfJpg($source);
|
||||
if (is_null($q)) {
|
||||
$this->/** @scrutinizer ignore-call */logLn(
|
||||
'Quality of source image could not be established (Imagick or GraphicsMagick is required). ' .
|
||||
'Sorry, no auto-limit functionality for you. '
|
||||
);
|
||||
if ($useDeprecatedDefaultQuality) {
|
||||
$q = $options['default-quality'];
|
||||
$this->/** @scrutinizer ignore-call */logLn(
|
||||
'Using default-quality (' . $q . ').'
|
||||
);
|
||||
} else {
|
||||
$q = $options['quality'];
|
||||
$this->/** @scrutinizer ignore-call */logLn(
|
||||
'Using supplied quality (' . $q . ').'
|
||||
);
|
||||
}
|
||||
|
||||
$this->qualityCouldNotBeDetected = true;
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Quality of jpeg: ' . $q . '. '
|
||||
);
|
||||
if ($q < $options['quality']) {
|
||||
$this->logLn(
|
||||
'Auto-limit result: ' . $q . ' ' .
|
||||
'(limiting applied).'
|
||||
);
|
||||
} else {
|
||||
$q = $options['quality'];
|
||||
$this->logLn(
|
||||
'Auto-limit result: ' . $q . ' ' .
|
||||
'(no limiting needed this time).'
|
||||
);
|
||||
}
|
||||
}
|
||||
$q = min($q, $options['max-quality']);
|
||||
} else {
|
||||
$this->logLn('Bypassing auto-limit (it is only active for jpegs)');
|
||||
$this->logLn('Quality: ' . $q . '. ');
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Quality: ' . $q . '. '
|
||||
);
|
||||
if (($this->getMimeTypeOfSource() == 'image/jpeg')) {
|
||||
$this->logLn(
|
||||
'Consider enabling "auto-limit" option. This will prevent unnecessary high quality'
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->calculatedQuality = $q;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFolderException;
|
||||
|
||||
/**
|
||||
* Trait for handling options
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait DestinationPreparationTrait
|
||||
{
|
||||
|
||||
abstract public function getDestination();
|
||||
abstract public function logLn($msg, $style = '');
|
||||
|
||||
/**
|
||||
* Create writable folder in provided path (if it does not exist already)
|
||||
*
|
||||
* @throws CreateDestinationFolderException if folder cannot be removed
|
||||
* @return void
|
||||
*/
|
||||
private function createWritableDestinationFolder()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
|
||||
$folder = dirname($destination);
|
||||
if (!file_exists($folder)) {
|
||||
$this->logLn('Destination folder does not exist. Creating folder: ' . $folder);
|
||||
// TODO: what if this is outside open basedir?
|
||||
// see http://php.net/manual/en/ini.core.php#ini.open-basedir
|
||||
|
||||
// Trying to create the given folder (recursively)
|
||||
if (!mkdir($folder, 0777, true)) {
|
||||
throw new CreateDestinationFolderException(
|
||||
'Failed creating folder. Check the permissions!',
|
||||
'Failed creating folder: ' . $folder . '. Check permissions!'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we can write file at destination.
|
||||
*
|
||||
* It is assumed that the folder already exists (that ::createWritableDestinationFolder() was called first)
|
||||
*
|
||||
* @throws CreateDestinationFileException if file cannot be created at destination
|
||||
* @return void
|
||||
*/
|
||||
private function checkDestinationWritable()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
$dirName = dirname($destination);
|
||||
|
||||
if (@is_writable($dirName) && @is_executable($dirName)) {
|
||||
// all is well
|
||||
return;
|
||||
}
|
||||
|
||||
// The above might fail on Windows, even though dir is writable
|
||||
// So, to be absolute sure that we cannot write, we make an actual write test (writing a dummy file)
|
||||
// No harm in doing that for non-Windows systems either.
|
||||
if (file_put_contents($destination, 'dummy') !== false) {
|
||||
// all is well, after all
|
||||
unlink($destination);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new CreateDestinationFileException(
|
||||
'Cannot create file: ' . basename($destination) . ' in dir:' . dirname($destination)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove existing destination.
|
||||
*
|
||||
* @throws CreateDestinationFileException if file cannot be removed
|
||||
* @return void
|
||||
*/
|
||||
private function removeExistingDestinationIfExists()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
if (file_exists($destination)) {
|
||||
// A file already exists in this folder...
|
||||
// We delete it, to make way for a new webp
|
||||
if (!unlink($destination)) {
|
||||
throw new CreateDestinationFileException(
|
||||
'Existing file cannot be removed: ' . basename($destination)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/LoggerTrait.php
vendored
Normal file
71
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/LoggerTrait.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
/**
|
||||
* Trait for providing logging capabilities.
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning logging.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait LoggerTrait
|
||||
{
|
||||
|
||||
/** @var \WebPConvert\Loggers\BaseLogger|null The logger (or null if not set) */
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Set logger
|
||||
*
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional) $logger
|
||||
* @return void
|
||||
*/
|
||||
public function setLogger($logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a line to the logger.
|
||||
*
|
||||
* @param string $msg The line to write.
|
||||
* @param string $style (optional) Ie "italic" or "bold"
|
||||
* @return void
|
||||
*/
|
||||
public function logLn($msg, $style = '')
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->logLn($msg, $style);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* New line
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ln()
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->ln();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the logger, without newline
|
||||
*
|
||||
* @param string $msg What to write.
|
||||
* @param string $style (optional) Ie "italic" or "bold"
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $style = '')
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$this->logger->log($msg, $style);
|
||||
}
|
||||
}
|
||||
}
|
||||
581
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/OptionsTrait.php
vendored
Normal file
581
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/BaseTraits/OptionsTrait.php
vendored
Normal file
@@ -0,0 +1,581 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
use WebPConvert\Convert\Converters\Stack;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
|
||||
|
||||
use WebPConvert\Options\GhostOption;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Trait for handling options
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait OptionsTrait
|
||||
{
|
||||
|
||||
abstract public function log($msg, $style = '');
|
||||
abstract public function logLn($msg, $style = '');
|
||||
abstract protected function getMimeTypeOfSource();
|
||||
|
||||
/** @var array Provided conversion options (array of simple objects)*/
|
||||
public $providedOptions;
|
||||
|
||||
/** @var array Calculated conversion options (merge of default options and provided options)*/
|
||||
protected $options;
|
||||
|
||||
/** @var Options */
|
||||
protected $options2;
|
||||
|
||||
/**
|
||||
* Get the "general" options (options that are standard in the meaning that they
|
||||
* are generally available (unless specifically marked as unsupported by a given converter)
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getGeneralOptions($imageType)
|
||||
{
|
||||
$isPng = ($imageType == 'png');
|
||||
|
||||
/*
|
||||
return [
|
||||
//new IntegerOption('auto-limit-adjustment', 5, -100, 100),
|
||||
new BooleanOption('log-call-arguments', false),
|
||||
new BooleanOption('skip', false),
|
||||
new BooleanOption('use-nice', false),
|
||||
new ArrayOption('jpeg', []),
|
||||
new ArrayOption('png', [])
|
||||
];*/
|
||||
|
||||
$introMd = 'https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/' .
|
||||
'converting/introduction-for-converting.md';
|
||||
|
||||
return OptionFactory::createOptions([
|
||||
['encoding', 'string', [
|
||||
'title' => 'Encoding',
|
||||
'description' => 'Set encoding for the webp. ' .
|
||||
'If you choose "auto", webp-convert will ' .
|
||||
'convert to both lossy and lossless and pick the smallest result',
|
||||
'default' => 'auto',
|
||||
'enum' => ['auto', 'lossy', 'lossless'],
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'links' => [['Guide', $introMd . '#auto-selecting-between-losslesslossy-encoding']],
|
||||
]
|
||||
]],
|
||||
['quality', 'int', [
|
||||
'title' => 'Quality (Lossy)',
|
||||
'description' =>
|
||||
'Quality for lossy encoding. ' .
|
||||
'In case you enable "auto-limit", you can consider this property a maximum quality.',
|
||||
'default' => ($isPng ? 85 : 75),
|
||||
'default-png' => 85,
|
||||
'default-jpeg' => 75,
|
||||
//'minimum' => 0,
|
||||
//'maximum' => 100,
|
||||
"oneOf" => [
|
||||
["type" => "number", "minimum" => 0, 'maximum' => 100],
|
||||
["type" => "string", "enum" => ["auto"]]
|
||||
],
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'display' => "option('encoding') != 'lossless'"
|
||||
]
|
||||
]],
|
||||
['auto-limit', 'boolean', [
|
||||
'title' => 'Auto-limit',
|
||||
'description' =>
|
||||
'Enable this option to prevent an unnecessarily high quality setting for low ' .
|
||||
'quality jpegs. It works by adjusting quality setting down to the quality of the jpeg. ' .
|
||||
'Converting ie a jpeg with quality:50 to ie quality:80 does not get you better quality ' .
|
||||
'than converting it to quality:80, but it does get you a much bigger file - so you ' .
|
||||
'really should enable this option.' . "\n\n" .
|
||||
'The option is ignored for PNG and never adjusts quality up. ' . "\n\n" .
|
||||
'The feature requires Imagick, ImageMagick or Gmagick in order to detect the quality of ' .
|
||||
'the jpeg. ' . "\n\n" .
|
||||
'PS: The "auto-limit" option is relative new. However, before this option, you could achieve ' .
|
||||
'the same by setting quality to "auto" and specifying a "max-quality" and a "default-quality". ' .
|
||||
'These are deprecated now, but still works.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'links' => [
|
||||
[
|
||||
'Guide',
|
||||
$introMd . '#preventing-unnecessarily-high-quality-setting-for-low-quality-jpegs'
|
||||
]
|
||||
],
|
||||
'display' => "option('encoding') != 'lossless'"
|
||||
]
|
||||
]],
|
||||
['alpha-quality', 'int', [
|
||||
'title' => 'Alpha quality',
|
||||
'description' =>
|
||||
'Quality of alpha channel. ' .
|
||||
'Often, there is no need for high quality transparency layer and in some cases you ' .
|
||||
'can tweak this all the way down to 10 and save a lot in file size. The option only ' .
|
||||
'has effect with lossy encoding, and of course only on images with transparency.',
|
||||
'default' => 85,
|
||||
'minimum' => 0,
|
||||
'maximum' => 100,
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'links' => [['Guide', $introMd . '#alpha-quality']],
|
||||
'display' => "(option('encoding') != 'lossless') && (imageType!='jpeg')"
|
||||
]
|
||||
]],
|
||||
['near-lossless', 'int', [
|
||||
'title' => '"Near lossless" quality',
|
||||
'description' =>
|
||||
'This option allows you to get impressively better compression for lossless encoding, with ' .
|
||||
'minimal impact on visual quality. The range is 0 (maximum preprocessing) to 100 (no ' .
|
||||
'preprocessing). Read the guide for more info.',
|
||||
'default' => 60,
|
||||
'minimum' => 0,
|
||||
'maximum' => 100,
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'links' => [['Guide', $introMd . '#near-lossless']],
|
||||
'display' => "option('encoding') != 'lossy'"
|
||||
]
|
||||
]],
|
||||
['metadata', 'string', [
|
||||
'title' => 'Metadata',
|
||||
'description' =>
|
||||
'Determines which metadata that should be copied over to the webp. ' .
|
||||
'Setting it to "all" preserves all metadata, setting it to "none" strips all metadata. ' .
|
||||
'*cwebp* can take a comma-separated list of which kinds of metadata that should be copied ' .
|
||||
'(ie "exif,icc"). *gd* will always remove all metadata and *ffmpeg* will always keep all ' .
|
||||
'metadata. The rest can either strip all or keep all (they will keep all, unless the option ' .
|
||||
'is set to *none*)',
|
||||
'default' => 'none',
|
||||
'ui' => [
|
||||
'component' => 'multi-select',
|
||||
'options' => ['all', 'none', 'exif', 'icc', 'xmp'],
|
||||
]
|
||||
// TODO: set regex validation
|
||||
]],
|
||||
['method', 'int', [
|
||||
'title' => 'Reduction effort (0-6)',
|
||||
'description' =>
|
||||
'Controls the trade off between encoding speed and the compressed file size and quality. ' .
|
||||
'Possible values range from 0 to 6. 0 is fastest. 6 results in best quality and compression. ' .
|
||||
'PS: The option corresponds to the "method" option in libwebp',
|
||||
'default' => 6,
|
||||
'minimum' => 0,
|
||||
'maximum' => 6,
|
||||
'ui' => [
|
||||
'component' => 'slider',
|
||||
'advanced' => true,
|
||||
]
|
||||
]],
|
||||
['sharp-yuv', 'boolean', [
|
||||
'title' => 'Sharp YUV',
|
||||
'description' =>
|
||||
'Better RGB->YUV color conversion (sharper and more accurate) at the expense of a little extra ' .
|
||||
'conversion time.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'links' => [
|
||||
['Ctrl.blog', 'https://www.ctrl.blog/entry/webp-sharp-yuv.html'],
|
||||
],
|
||||
]
|
||||
]],
|
||||
['auto-filter', 'boolean', [
|
||||
'title' => 'Auto-filter',
|
||||
'description' =>
|
||||
'Turns auto-filter on. ' .
|
||||
'This algorithm will spend additional time optimizing the filtering strength to reach a well-' .
|
||||
'balanced quality. Unfortunately, it is extremely expensive in terms of computation. It takes ' .
|
||||
'about 5-10 times longer to do a conversion. A 1MB picture which perhaps typically takes about ' .
|
||||
'2 seconds to convert, will takes about 15 seconds to convert with auto-filter. ',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
]
|
||||
]],
|
||||
['low-memory', 'boolean', [
|
||||
'title' => 'Low memory',
|
||||
'description' =>
|
||||
'Reduce memory usage of lossy encoding at the cost of ~30% longer encoding time and marginally ' .
|
||||
'larger output size. Only effective when the *method* option is 3 or more. Read more in ' .
|
||||
'[the docs](https://developers.google.com/speed/webp/docs/cwebp)',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'display' => "(option('encoding') != 'lossless') && (option('method')>2)"
|
||||
]
|
||||
]],
|
||||
['preset', 'string', [
|
||||
'title' => 'Preset',
|
||||
'description' =>
|
||||
'Using a preset will set many of the other options to suit a particular type of ' .
|
||||
'source material. It even overrides them. It does however not override the quality option. ' .
|
||||
'"none" means that no preset will be set',
|
||||
'default' => 'none',
|
||||
'enum' => ['none', 'default', 'photo', 'picture', 'drawing', 'icon', 'text'],
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'advanced' => true,
|
||||
]
|
||||
]],
|
||||
['size-in-percentage', 'int', ['default' => null, 'minimum' => 0, 'maximum' => 100, 'allow-null' => true]],
|
||||
['skip', 'boolean', ['default' => false]],
|
||||
['log-call-arguments', 'boolean', ['default' => false]],
|
||||
// TODO: use-nice should not be a "general" option
|
||||
//['use-nice', 'boolean', ['default' => false]],
|
||||
['jpeg', 'array', ['default' => []]],
|
||||
['png', 'array', ['default' => []]],
|
||||
|
||||
// Deprecated options
|
||||
['default-quality', 'int', [
|
||||
'default' => ($isPng ? 85 : 75),
|
||||
'minimum' => 0,
|
||||
'maximum' => 100,
|
||||
'deprecated' => true]
|
||||
],
|
||||
['max-quality', 'int', ['default' => 85, 'minimum' => 0, 'maximum' => 100, 'deprecated' => true]],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique options for a converter
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create options.
|
||||
*
|
||||
* The options created here will be available to all converters.
|
||||
* Individual converters may add options by overriding this method.
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createOptions($imageType = 'png')
|
||||
{
|
||||
$this->options2 = new Options();
|
||||
$this->options2->addOptions(... $this->getGeneralOptions($imageType));
|
||||
$this->options2->addOptions(... $this->getUniqueOptions($imageType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set "provided options" (options provided by the user when calling convert().
|
||||
*
|
||||
* This also calculates the protected options array, by merging in the default options, merging
|
||||
* jpeg and png options and merging prefixed options (such as 'vips-quality').
|
||||
* The resulting options array are set in the protected property $this->options and can be
|
||||
* retrieved using the public ::getOptions() function.
|
||||
*
|
||||
* @param array $providedOptions (optional)
|
||||
* @return void
|
||||
*/
|
||||
public function setProvidedOptions($providedOptions = [])
|
||||
{
|
||||
$imageType = ($this->getMimeTypeOfSource() == 'image/png' ? 'png' : 'jpeg');
|
||||
$this->createOptions($imageType);
|
||||
|
||||
$this->providedOptions = $providedOptions;
|
||||
|
||||
if (isset($this->providedOptions['png'])) {
|
||||
if ($this->getMimeTypeOfSource() == 'image/png') {
|
||||
$this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['png']);
|
||||
// $this->logLn(print_r($this->providedOptions, true));
|
||||
unset($this->providedOptions['png']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->providedOptions['jpeg'])) {
|
||||
if ($this->getMimeTypeOfSource() == 'image/jpeg') {
|
||||
$this->providedOptions = array_merge($this->providedOptions, $this->providedOptions['jpeg']);
|
||||
unset($this->providedOptions['jpeg']);
|
||||
}
|
||||
}
|
||||
|
||||
// merge down converter-prefixed options
|
||||
$converterId = self::getConverterId();
|
||||
$strLen = strlen($converterId);
|
||||
foreach ($this->providedOptions as $optionKey => $optionValue) {
|
||||
if (substr($optionKey, 0, $strLen + 1) == ($converterId . '-')) {
|
||||
$this->providedOptions[substr($optionKey, $strLen + 1)] = $optionValue;
|
||||
unset($this->providedOptions[$optionKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create options (Option objects)
|
||||
foreach ($this->providedOptions as $optionId => $optionValue) {
|
||||
$this->options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
//$this->logLn(print_r($this->options2->getOptions(), true));
|
||||
//$this->logLn($this->options2->getOption('hello'));
|
||||
|
||||
// Create flat associative array of options
|
||||
$this->options = $this->options2->getOptions();
|
||||
|
||||
// - Merge $defaultOptions into provided options
|
||||
//$this->options = array_merge($this->getDefaultOptions(), $this->providedOptions);
|
||||
|
||||
//$this->logOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resulting options after merging provided options with default options.
|
||||
*
|
||||
* Note that the defaults depends on the mime type of the source. For example, the default value for quality
|
||||
* is "auto" for jpegs, and 85 for pngs.
|
||||
*
|
||||
* @return array An associative array of options: ['metadata' => 'none', ...]
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change an option specifically.
|
||||
*
|
||||
* This method is probably rarely neeeded. We are using it to change the "encoding" option temporarily
|
||||
* in the EncodingAutoTrait.
|
||||
*
|
||||
* @param string $id Id of option (ie "metadata")
|
||||
* @param mixed $value The new value.
|
||||
* @return void
|
||||
*/
|
||||
protected function setOption($id, $value)
|
||||
{
|
||||
$this->options[$id] = $value;
|
||||
$this->options2->setOrCreateOption($id, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check options.
|
||||
*
|
||||
* @throws InvalidOptionTypeException if an option have wrong type
|
||||
* @throws InvalidOptionValueException if an option value is out of range
|
||||
* @throws ConversionSkippedException if 'skip' option is set to true
|
||||
* @return void
|
||||
*/
|
||||
protected function checkOptions()
|
||||
{
|
||||
$this->options2->check();
|
||||
|
||||
if ($this->options['skip']) {
|
||||
if (($this->getMimeTypeOfSource() == 'image/png') && isset($this->options['png']['skip'])) {
|
||||
throw new ConversionSkippedException(
|
||||
'skipped conversion (configured to do so for PNG)'
|
||||
);
|
||||
} else {
|
||||
throw new ConversionSkippedException(
|
||||
'skipped conversion (configured to do so)'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function logOptions()
|
||||
{
|
||||
$this->logLn('');
|
||||
$this->logLn('Options:');
|
||||
$this->logLn('------------');
|
||||
|
||||
$unsupported = $this->getUnsupportedDefaultOptions();
|
||||
$received = [];
|
||||
$implicitlySet = [];
|
||||
foreach ($this->options2->getOptionsMap() as $id => $option) {
|
||||
if (in_array($id, [
|
||||
'png', 'jpeg', '_skip_input_check', '_suppress_success_message', 'skip', 'log_call_arguments'
|
||||
])) {
|
||||
continue;
|
||||
}
|
||||
if ($option->isValueExplicitlySet()) {
|
||||
$received[] = $option;
|
||||
} else {
|
||||
if (($option instanceof GhostOption) || in_array($id, $unsupported)) {
|
||||
//$received[] = $option;
|
||||
} else {
|
||||
if (!$option->isDeprecated()) {
|
||||
$implicitlySet[] = $option;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($received) > 0) {
|
||||
foreach ($received as $option) {
|
||||
$this->log('- ' . $option->getId() . ': ');
|
||||
if ($option instanceof GhostOption) {
|
||||
$this->log(' (unknown to ' . $this->getConverterId() . ')', 'bold');
|
||||
$this->logLn('');
|
||||
continue;
|
||||
}
|
||||
$this->log($option->getValueForPrint());
|
||||
if ($option->isDeprecated()) {
|
||||
$this->log(' (deprecated)', 'bold');
|
||||
}
|
||||
if (in_array($option->getId(), $unsupported)) {
|
||||
if ($this instanceof Stack) {
|
||||
//$this->log(' *(passed on)*');
|
||||
} else {
|
||||
$this->log(' (unsupported by ' . $this->getConverterId() . ')', 'bold');
|
||||
}
|
||||
}
|
||||
$this->logLn('');
|
||||
}
|
||||
$this->logLn('');
|
||||
$this->logLn(
|
||||
'Note that these are the resulting options after merging down the "jpeg" and "png" options and any ' .
|
||||
'converter-prefixed options'
|
||||
);
|
||||
}
|
||||
|
||||
if (count($implicitlySet) > 0) {
|
||||
$this->logLn('');
|
||||
$this->logLn('Defaults:');
|
||||
$this->logLn('------------');
|
||||
$this->logLn(
|
||||
'The following options was not set, so using the following defaults:'
|
||||
);
|
||||
foreach ($implicitlySet as $option) {
|
||||
$this->log('- ' . $option->getId() . ': ');
|
||||
$this->log($option->getValueForPrint());
|
||||
/*if ($option instanceof GhostOption) {
|
||||
$this->log(' **(ghost)**');
|
||||
}*/
|
||||
$this->logLn('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to be overridden by converters
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getUnsupportedGeneralOptions()
|
||||
{
|
||||
return $this->getUnsupportedDefaultOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique option definitions.
|
||||
*
|
||||
* Gets definitions of the converters "unique" options (that is, those options that
|
||||
* are not general). It was added in order to give GUI's a way to automatically adjust
|
||||
* their setting screens.
|
||||
*
|
||||
* @param bool $filterOutOptionsWithoutUI If options without UI defined should be filtered out
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
*/
|
||||
public function getUniqueOptionDefinitions($filterOutOptionsWithoutUI = true, $imageType = 'jpeg')
|
||||
{
|
||||
$uniqueOptions = new Options();
|
||||
//$uniqueOptions->addOptions(... $this->getUniqueOptions($imageType));
|
||||
foreach ($this->getUniqueOptions($imageType) as $uoption) {
|
||||
$uoption->setId(self::getConverterId() . '-' . $uoption->getId());
|
||||
$uniqueOptions->addOption($uoption);
|
||||
}
|
||||
|
||||
$optionDefinitions = $uniqueOptions->getDefinitions();
|
||||
if ($filterOutOptionsWithoutUI) {
|
||||
$optionDefinitions = array_filter($optionDefinitions, function ($value) {
|
||||
return !is_null($value['ui']);
|
||||
});
|
||||
$optionDefinitions = array_values($optionDefinitions); // re-index
|
||||
}
|
||||
return $optionDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general option definitions.
|
||||
*
|
||||
* Gets definitions of all general options (not just the ones supported by current converter)
|
||||
* For UI's, as a way to automatically adjust their setting screens.
|
||||
*
|
||||
* @param bool $filterOutOptionsWithoutUI If options without UI defined should be filtered out
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
*/
|
||||
public function getGeneralOptionDefinitions($filterOutOptionsWithoutUI = true, $imageType = 'jpeg')
|
||||
{
|
||||
$generalOptions = new Options();
|
||||
$generalOptions->addOptions(... $this->getGeneralOptions($imageType));
|
||||
//$generalOptions->setUI($this->getUIForGeneralOptions($imageType));
|
||||
$optionDefinitions = $generalOptions->getDefinitions();
|
||||
if ($filterOutOptionsWithoutUI) {
|
||||
$optionDefinitions = array_filter($optionDefinitions, function ($value) {
|
||||
return !is_null($value['ui']);
|
||||
});
|
||||
$optionDefinitions = array_values($optionDefinitions); // re-index
|
||||
}
|
||||
return $optionDefinitions;
|
||||
}
|
||||
|
||||
public function getSupportedGeneralOptions($imageType = 'png')
|
||||
{
|
||||
$unsupportedGeneral = $this->getUnsupportedDefaultOptions();
|
||||
$generalOptionsArr = $this->getGeneralOptions($imageType);
|
||||
$supportedIds = [];
|
||||
foreach ($generalOptionsArr as $i => $option) {
|
||||
if (in_array($option->getId(), $unsupportedGeneral)) {
|
||||
unset($generalOptionsArr[$i]);
|
||||
}
|
||||
}
|
||||
return $generalOptionsArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general option definitions.
|
||||
*
|
||||
* Gets definitions of the converters "general" options. (that is, those options that
|
||||
* It was added in order to give GUI's a way to automatically adjust their setting screens.
|
||||
*
|
||||
* @param string $imageType (png | jpeg) The image type - determines the defaults
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
*/
|
||||
public function getSupportedGeneralOptionDefinitions($imageType = 'png')
|
||||
{
|
||||
$generalOptions = new Options();
|
||||
$generalOptions->addOptions(... $this->getSupportedGeneralOptions($imageType));
|
||||
return $generalOptions->getDefinitions();
|
||||
}
|
||||
|
||||
public function getSupportedGeneralOptionIds()
|
||||
{
|
||||
$supportedGeneralOptions = $this->getSupportedGeneralOptions();
|
||||
$supportedGeneralIds = [];
|
||||
foreach ($supportedGeneralOptions as $option) {
|
||||
$supportedGeneralIds[] = $option->getId();
|
||||
}
|
||||
return $supportedGeneralIds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
|
||||
/**
|
||||
* Trait for handling warnings (by logging them)
|
||||
*
|
||||
* This trait is currently only used in the AbstractConverter class. It has been extracted into a
|
||||
* trait in order to bundle the methods concerning options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait WarningLoggerTrait
|
||||
{
|
||||
abstract public function logLn($msg, $style = '');
|
||||
|
||||
/** @var string|array|null Previous error handler (stored in order to be able pass warnings on) */
|
||||
private $previousErrorHandler;
|
||||
|
||||
/** @var boolean Suppress ALL warnings? (both from log and from bubbling up) */
|
||||
private $suppressWarnings;
|
||||
|
||||
/** @var int Count number of warnings */
|
||||
private $warningCounter;
|
||||
|
||||
/**
|
||||
* Handle warnings and notices during conversion by logging them and passing them on.
|
||||
*
|
||||
* The function is a callback used with "set_error_handler".
|
||||
* It is declared public because it needs to be accessible from the point where the warning is triggered.
|
||||
*
|
||||
* PS: The fifth parameter ($errcontext) of an error handler is deprecated since PHP 7.2, however we have
|
||||
* it here to avoid calling another error handler with too few parameters (see #266)
|
||||
*
|
||||
* @param integer $errno
|
||||
* @param string $errstr
|
||||
* @param string $errfile
|
||||
* @param integer $errline
|
||||
* @param array $errcontext
|
||||
*
|
||||
* @return false|null|void
|
||||
*/
|
||||
public function warningHandler($errno, $errstr, $errfile, $errline, $errcontext = null)
|
||||
{
|
||||
/*
|
||||
We do NOT do the following (even though it is generally recommended):
|
||||
|
||||
if (!(error_reporting() & $errno)) {
|
||||
// This error code is not included in error_reporting, so let it fall
|
||||
// through to the standard PHP error handler
|
||||
return false;
|
||||
}
|
||||
|
||||
- Because we want to log all warnings and errors (also the ones that was suppressed with @)
|
||||
https://secure.php.net/manual/en/language.operators.errorcontrol.php
|
||||
|
||||
If we were to decide suppressing the ones with @, I could do this:
|
||||
|
||||
if (error_reporting() == 0) {
|
||||
/// @ sign temporary disabled error reporting
|
||||
return;
|
||||
}
|
||||
[https://stackoverflow.com/questions/7380782/error-suppression-operator-and-set-error-handler]
|
||||
|
||||
However, that would also disable the warnings on systems with error reporting set to E_NONE.
|
||||
And I really want the conversion log file to contain these warnings on all systems.
|
||||
|
||||
If it was possible to suppress the warnings with @ without suppressing warnings on systems
|
||||
with error reporting set to E_NONE, I would do that.
|
||||
*/
|
||||
|
||||
$this->warningCounter++;
|
||||
if ($this->suppressWarnings) {
|
||||
return;
|
||||
}
|
||||
|
||||
$errorTypes = [
|
||||
E_WARNING => "Warning",
|
||||
E_NOTICE => "Notice",
|
||||
E_STRICT => "Strict Notice",
|
||||
E_DEPRECATED => "Deprecated",
|
||||
E_USER_DEPRECATED => "User Deprecated",
|
||||
|
||||
/*
|
||||
The following can never be catched by a custom error handler:
|
||||
E_PARSE, E_ERROR, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
|
||||
|
||||
We do do not currently trigger the following:
|
||||
E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
|
||||
|
||||
But we may want to do that at some point, like this:
|
||||
trigger_error('Your version of Gd is very old', E_USER_WARNING);
|
||||
in that case, remember to add them to this array
|
||||
*/
|
||||
];
|
||||
|
||||
if (isset($errorTypes[$errno])) {
|
||||
$errType = $errorTypes[$errno];
|
||||
} else {
|
||||
$errType = "Unknown error/warning/notice ($errno)";
|
||||
}
|
||||
|
||||
$msg = $errType . ': ' . $errstr . ' in ' . $errfile . ', line ' . $errline . ', PHP ' . PHP_VERSION .
|
||||
' (' . PHP_OS . ')';
|
||||
$this->logLn('');
|
||||
$this->logLn($msg, 'italic');
|
||||
$this->logLn('');
|
||||
|
||||
if (!is_null($this->previousErrorHandler)) {
|
||||
// If previousErrorHandler is this very error handler, exit to avoid recursion
|
||||
// (this could happen if ::activateWarningLogger() were called twice)
|
||||
if (is_array($this->previousErrorHandler) &&
|
||||
isset($this->previousErrorHandler[0]) &&
|
||||
($this->previousErrorHandler[0] == $this)
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate warning logger.
|
||||
*
|
||||
* Sets the error handler and stores the previous so our error handler can bubble up warnings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function activateWarningLogger()
|
||||
{
|
||||
$this->suppressWarnings = false;
|
||||
$this->warningCounter = 0;
|
||||
$this->previousErrorHandler = set_error_handler(
|
||||
array($this, "warningHandler"),
|
||||
E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate warning logger.
|
||||
*
|
||||
* Restores the previous error handler.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deactivateWarningLogger()
|
||||
{
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
protected function disableWarningsTemporarily()
|
||||
{
|
||||
$this->suppressWarnings = true;
|
||||
}
|
||||
|
||||
protected function reenableWarnings()
|
||||
{
|
||||
$this->suppressWarnings = false;
|
||||
}
|
||||
|
||||
protected function getWarningCount()
|
||||
{
|
||||
return $this->warningCounter;
|
||||
}
|
||||
|
||||
protected function resetWarningCount()
|
||||
{
|
||||
$this->warningCounter = 0;
|
||||
}
|
||||
}
|
||||
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-fbsd
vendored
Normal file
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-fbsd
vendored
Normal file
Binary file not shown.
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-solaris
vendored
Normal file
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-060-solaris
vendored
Normal file
Binary file not shown.
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-061-linux-x86-64
vendored
Normal file
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-061-linux-x86-64
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-linux-x86-64
vendored
Normal file
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-linux-x86-64
vendored
Normal file
Binary file not shown.
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-mac-10_15
vendored
Normal file
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-110-mac-10_15
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-120-linux-x86-64
vendored
Normal file
BIN
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Binaries/cwebp-120-linux-x86-64
vendored
Normal file
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Helpers\PhpIniSizes;
|
||||
|
||||
/**
|
||||
* Trait for converters that works by uploading to a cloud service.
|
||||
*
|
||||
* The trait adds a method for checking against upload limits.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait CloudConverterTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Test that filesize is below "upload_max_filesize" and "post_max_size" values in php.ini.
|
||||
*
|
||||
* @param string $iniSettingId Id of ini setting (ie "upload_max_filesize")
|
||||
*
|
||||
* @throws ConversionFailedException if filesize is larger than the ini setting
|
||||
* @return void
|
||||
*/
|
||||
private function checkFileSizeVsIniSetting($iniSettingId)
|
||||
{
|
||||
$fileSize = @filesize($this->source);
|
||||
if ($fileSize === false) {
|
||||
return;
|
||||
}
|
||||
$sizeInIni = PhpIniSizes::getIniBytes($iniSettingId);
|
||||
if ($sizeInIni === false) {
|
||||
// Not sure if we should throw an exception here, or not...
|
||||
return;
|
||||
}
|
||||
if ($sizeInIni < $fileSize) {
|
||||
throw new ConversionFailedException(
|
||||
'File is larger than your ' . $iniSettingId . ' (set in your php.ini). File size:' .
|
||||
round($fileSize / 1024) . ' kb. ' .
|
||||
$iniSettingId . ' in php.ini: ' . ini_get($iniSettingId) .
|
||||
' (parsed as ' . round($sizeInIni / 1024) . ' kb)'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check convertability of cloud converters (that file is not bigger than limits set in php.ini).
|
||||
*
|
||||
* Performs the same as ::Convertability(). It is here so converters that overrides the
|
||||
* ::Convertability() still has a chance to do the checks.
|
||||
*
|
||||
* @throws ConversionFailedException if filesize is larger than "upload_max_filesize" or "post_max_size"
|
||||
* @return void
|
||||
*/
|
||||
public function checkConvertabilityCloudConverterTrait()
|
||||
{
|
||||
$this->checkFileSizeVsIniSetting('upload_max_filesize');
|
||||
$this->checkFileSizeVsIniSetting('post_max_size');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check convertability of cloud converters (file upload limits).
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$this->checkConvertabilityCloudConverterTrait();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
|
||||
/**
|
||||
* Trait for converters that works by uploading to a cloud service.
|
||||
*
|
||||
* The trait adds a method for checking against upload limits.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait CurlTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Check basis operationality for converters relying on curl.
|
||||
*
|
||||
* Performs the same as ::checkOperationality(). It is here so converters that overrides the
|
||||
* ::checkOperationality() still has a chance to do the checks.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationalityForCurlTrait()
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
throw new SystemRequirementsNotMetException('Required cURL extension is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('curl_init')) {
|
||||
throw new SystemRequirementsNotMetException('Required url_init() function is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('curl_file_create')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Required curl_file_create() function is not available (requires PHP > 5.5).'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check basis operationality for converters relying on curl
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityForCurlTrait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init curl.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if curl could not be initialized
|
||||
* @return resource|\CurlHandle curl handle (from PHP8: CurlHandle)
|
||||
*/
|
||||
protected static function initCurl()
|
||||
{
|
||||
// Get curl handle
|
||||
$ch = \curl_init();
|
||||
if ($ch === false) {
|
||||
throw new SystemRequirementsNotMetException('Could not initialise cURL.');
|
||||
}
|
||||
return $ch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
//namespace WebPConvert\Convert\Converters\BaseTraits;
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
/**
|
||||
* Trait for converters that supports lossless encoding and thus the "lossless:auto" option.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait EncodingAutoTrait
|
||||
{
|
||||
|
||||
abstract protected function doActualConvert();
|
||||
abstract public function getSource();
|
||||
abstract public function getDestination();
|
||||
abstract public function setDestination($destination);
|
||||
abstract public function getOptions();
|
||||
abstract protected function setOption($optionName, $optionValue);
|
||||
abstract protected function logLn($msg, $style = '');
|
||||
abstract protected function log($msg, $style = '');
|
||||
abstract protected function ln();
|
||||
abstract protected function logReduction($source, $destination);
|
||||
|
||||
public function supportsLossless()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Default is to not pass "lossless:auto" on, but implement it.
|
||||
*
|
||||
* The Stack converter passes it on (it does not even use this trait)
|
||||
* WPC currently implements it, but this might be configurable in the future.
|
||||
*
|
||||
*/
|
||||
public function passOnEncodingAuto()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertTwoAndSelectSmallest()
|
||||
{
|
||||
$destination = $this->getDestination();
|
||||
$destinationLossless = $destination . '.lossless.webp';
|
||||
$destinationLossy = $destination . '.lossy.webp';
|
||||
|
||||
$this->logLn(
|
||||
'Encoding is set to auto - converting to both lossless and lossy and selecting the smallest file'
|
||||
);
|
||||
|
||||
$this->ln();
|
||||
$this->logLn('Converting to lossy');
|
||||
$this->setDestination($destinationLossy);
|
||||
$this->setOption('encoding', 'lossy');
|
||||
$this->doActualConvert();
|
||||
$this->log('Reduction: ');
|
||||
$this->logReduction($this->getSource(), $destinationLossy);
|
||||
$this->ln();
|
||||
|
||||
$this->logLn('Converting to lossless');
|
||||
$this->setDestination($destinationLossless);
|
||||
$this->setOption('encoding', 'lossless');
|
||||
$this->doActualConvert();
|
||||
$this->log('Reduction: ');
|
||||
$this->logReduction($this->getSource(), $destinationLossless);
|
||||
$this->ln();
|
||||
|
||||
if (filesize($destinationLossless) > filesize($destinationLossy)) {
|
||||
$this->logLn('Picking lossy');
|
||||
unlink($destinationLossless);
|
||||
rename($destinationLossy, $destination);
|
||||
} else {
|
||||
$this->logLn('Picking lossless');
|
||||
unlink($destinationLossy);
|
||||
rename($destinationLossless, $destination);
|
||||
}
|
||||
$this->setDestination($destination);
|
||||
$this->setOption('encoding', 'auto');
|
||||
}
|
||||
|
||||
protected function runActualConvert()
|
||||
{
|
||||
if (!$this->passOnEncodingAuto() && ($this->getOptions()['encoding'] == 'auto') && $this->supportsLossless()) {
|
||||
$this->convertTwoAndSelectSmallest();
|
||||
} else {
|
||||
$this->doActualConvert();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/ExecTrait.php
vendored
Normal file
107
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/ConverterTraits/ExecTrait.php
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters\ConverterTraits;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
/**
|
||||
* Trait for converters that uses exec() or similar
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
trait ExecTrait
|
||||
{
|
||||
|
||||
abstract protected function logLn($msg, $style = '');
|
||||
|
||||
|
||||
/**
|
||||
* Helper function for examining if "nice" command is available
|
||||
*
|
||||
* @return boolean true if nice is available
|
||||
*/
|
||||
protected static function hasNiceSupport()
|
||||
{
|
||||
ExecWithFallback::exec("nice 2>&1", $niceOutput);
|
||||
|
||||
if (is_array($niceOutput) && isset($niceOutput[0])) {
|
||||
if (preg_match('/usage/', $niceOutput[0]) || (preg_match('/^\d+$/', $niceOutput[0]))) {
|
||||
/*
|
||||
* Nice is available - default niceness (+10)
|
||||
* https://www.lifewire.com/uses-of-commands-nice-renice-2201087
|
||||
* https://www.computerhope.com/unix/unice.htm
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false; // to satisfy phpstan
|
||||
}
|
||||
|
||||
protected function checkNiceSupport()
|
||||
{
|
||||
$ok = self::hasNiceSupport();
|
||||
if ($ok) {
|
||||
$this->logLn('Tested "nice" command - it works :)');
|
||||
} else {
|
||||
$this->logLn(
|
||||
'**No "nice" support. To save a few ms, you can disable the "use-nice" option.**'
|
||||
);
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
||||
protected static function niceOption()
|
||||
{
|
||||
return ['use-nice', 'boolean', [
|
||||
'title' => 'Use nice',
|
||||
'description' =>
|
||||
'If *use-nice* is set, it will be examined if the *nice* command is available. ' .
|
||||
'If it is, the binary is executed using *nice*. This assigns low priority to the process and ' .
|
||||
'will save system resources - but result in slower conversion.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs output from the exec call.
|
||||
*
|
||||
* @param array $output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function logExecOutput($output)
|
||||
{
|
||||
if (is_array($output) && count($output) > 0) {
|
||||
$this->logLn('');
|
||||
$this->logLn('Output:', 'italic');
|
||||
foreach ($output as $line) {
|
||||
$this->logLn(print_r($line, true));
|
||||
}
|
||||
$this->logLn('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check basic operationality of exec converters (that the "exec" or similar function is available)
|
||||
*
|
||||
* @throws WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationalityExecTrait()
|
||||
{
|
||||
if (!ExecWithFallback::anyAvailable()) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'exec() is not enabled (nor is alternative methods, such as proc_open())'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
980
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php
vendored
Normal file
980
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Cwebp.php
vendored
Normal file
@@ -0,0 +1,980 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
use LocateBinaries\LocateBinaries;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling cwebp binary.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Cwebp extends AbstractConverter
|
||||
{
|
||||
|
||||
use EncodingAutoTrait;
|
||||
use ExecTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
$binariesForOS = [];
|
||||
if (isset(self::$suppliedBinariesInfo[PHP_OS])) {
|
||||
foreach (self::$suppliedBinariesInfo[PHP_OS] as $i => list($file, $hash, $version)) {
|
||||
$binariesForOS[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption(),
|
||||
['try-cwebp', 'boolean', [
|
||||
'title' => 'Try plain cwebp command',
|
||||
'description' =>
|
||||
'If set, the converter will try executing "cwebp -version". In case it succeeds, ' .
|
||||
'and the version is higher than those working cwebps found using other methods, ' .
|
||||
'the conversion will be done by executing this cwebp.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['try-discovering-cwebp', 'boolean', [
|
||||
'title' => 'Try discovering cwebp binary',
|
||||
'description' =>
|
||||
'If set, the converter will try to discover installed cwebp binaries using a "which -a cwebp" ' .
|
||||
'command, or in case that fails, a "whereis -b cwebp" command. These commands will find ' .
|
||||
'cwebp binaries residing in PATH',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['try-common-system-paths', 'boolean', [
|
||||
'title' => 'Try locating cwebp in common system paths',
|
||||
'description' =>
|
||||
'If set, the converter will look for a cwebp binaries residing in common system locations ' .
|
||||
'such as "/usr/bin/cwebp". If such exist, it is assumed that they are valid cwebp binaries. ' .
|
||||
'A version check will be run on the binaries found (they are executed with the "-version" flag. ' .
|
||||
'The cwebp with the highest version found using this method and the other enabled methods will ' .
|
||||
'be used for the actual conversion.' .
|
||||
'Note: All methods for discovering cwebp binaries are per default enabled. You can save a few ' .
|
||||
'microseconds by disabling some, but it is probably not worth it, as your ' .
|
||||
'setup will then become less resilient to system changes.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['try-supplied-binary-for-os', 'boolean', [
|
||||
'title' => 'Try precompiled cwebp binaries',
|
||||
'description' =>
|
||||
'If set, the converter will try use a precompiled cwebp binary that comes with webp-convert. ' .
|
||||
'But only if it has a higher version that those found by other methods. As the library knows ' .
|
||||
'the versions of its bundled binaries, no additional time is spent executing them with the ' .
|
||||
'"-version" parameter. The binaries are hash-checked before executed. ' .
|
||||
'The library btw. comes with several versions of precompiled cwebps because they have different ' .
|
||||
'dependencies - some works on some systems and others on others.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['skip-these-precompiled-binaries', 'string', [
|
||||
'title' => 'Skip these precompiled binaries',
|
||||
'description' =>
|
||||
'',
|
||||
'default' => '',
|
||||
'ui' => [
|
||||
'component' => 'multi-select',
|
||||
'advanced' => true,
|
||||
'options' => $binariesForOS,
|
||||
'display' => "option('cwebp-try-supplied-binary-for-os') == true"
|
||||
]
|
||||
|
||||
]],
|
||||
['rel-path-to-precompiled-binaries', 'string', [
|
||||
'title' => 'Rel path to precompiled binaries',
|
||||
'description' =>
|
||||
'',
|
||||
'default' => './Binaries',
|
||||
'ui' => [
|
||||
'component' => '',
|
||||
'advanced' => true,
|
||||
'display' => "option('cwebp-try-supplied-binary-for-os') == true"
|
||||
],
|
||||
'sensitive' => true
|
||||
]],
|
||||
['command-line-options', 'string', [
|
||||
'title' => 'Command line options',
|
||||
'description' =>
|
||||
'',
|
||||
'default' => '',
|
||||
'ui' => [
|
||||
'component' => 'input',
|
||||
'advanced' => true,
|
||||
]
|
||||
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// OS-specific binaries included in this library, along with hashes
|
||||
// If other binaries are going to be added, notice that the first argument is what PHP_OS returns.
|
||||
// (possible values, see here: https://stackoverflow.com/questions/738823/possible-values-for-php-os)
|
||||
// Got the precompiled binaries here: https://developers.google.com/speed/webp/docs/precompiled
|
||||
// Note when changing binaries:
|
||||
// 1: Do NOT use "." in filename. It causes unzipping to fail on some hosts
|
||||
// 2: Set permission to 775. 755 causes unzipping to fail on some hosts
|
||||
private static $suppliedBinariesInfo = [
|
||||
'WINNT' => [
|
||||
['cwebp-120-windows-x64.exe', '2849fd06012a9eb311b02a4f8918ae4b16775693bc21e95f4cc6a382eac299f9', '1.2.0'],
|
||||
|
||||
// Keep the 1.1.0 version a while, in case some may have problems with the 1.2.0 version
|
||||
['cwebp-110-windows-x64.exe', '442682869402f92ad2c8b3186c02b0ea6d6da68d2f908df38bf905b3411eb9fb', '1.1.0'],
|
||||
],
|
||||
'Darwin' => [
|
||||
['cwebp-110-mac-10_15', 'bfce742da09b959f9f2929ba808fed9ade25c8025530434b6a47d217a6d2ceb5', '1.1.0'],
|
||||
],
|
||||
'SunOS' => [
|
||||
// Got this from ewww Wordpress plugin, which unfortunately still uses the old 0.6.0 versions
|
||||
// Can you help me get a 1.0.3 version?
|
||||
['cwebp-060-solaris', '1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f', '0.6.0']
|
||||
],
|
||||
'FreeBSD' => [
|
||||
// Got this from ewww Wordpress plugin, which unfortunately still uses the old 0.6.0 versions
|
||||
// Can you help me get a 1.0.3 version?
|
||||
['cwebp-060-fbsd', 'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573', '0.6.0']
|
||||
],
|
||||
'Linux' => [
|
||||
|
||||
// PS: Some experience the following error with 1.20:
|
||||
// /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found
|
||||
// (see #278)
|
||||
|
||||
['cwebp-120-linux-x86-64', 'f1b7dc03e95535a6b65852de07c0404be4dba078af48369f434ee39b2abf8f4e', '1.2.0'],
|
||||
|
||||
// As some experience the an error with 1.20 (see #278), we keep the 1.10
|
||||
['cwebp-110-linux-x86-64', '1603b07b592876dd9fdaa62b44aead800234c9474ff26dc7dd01bc0f4785c9c6', '1.1.0'],
|
||||
|
||||
// Statically linked executable
|
||||
// It may be that it on some systems works, where the dynamically linked does not (see #196)
|
||||
[
|
||||
'cwebp-103-linux-x86-64-static',
|
||||
'ab96f01b49336da8b976c498528080ff614112d5985da69943b48e0cb1c5228a',
|
||||
'1.0.3'
|
||||
],
|
||||
|
||||
// Old executable for systems in case all of the above fails
|
||||
['cwebp-061-linux-x86-64', '916623e5e9183237c851374d969aebdb96e0edc0692ab7937b95ea67dc3b2568', '0.6.1'],
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Check all hashes of the precompiled binaries.
|
||||
*
|
||||
* This isn't used when converting, but can be used as a startup check.
|
||||
*/
|
||||
public static function checkAllHashes()
|
||||
{
|
||||
foreach (self::$suppliedBinariesInfo as $os => $arr) {
|
||||
foreach ($arr as $i => list($filename, $expectedHash)) {
|
||||
$actualHash = hash_file("sha256", __DIR__ . '/Binaries/' . $filename);
|
||||
if ($expectedHash != $actualHash) {
|
||||
throw new \Exception(
|
||||
'Hash for ' . $filename . ' is incorrect! ' .
|
||||
'Checksum is: ' . $actualHash . ', ' .
|
||||
', but expected: ' . $expectedHash .
|
||||
'. Did you transfer with FTP, but not in binary mode? '
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
$options = $this->options;
|
||||
if (!$options['try-supplied-binary-for-os'] &&
|
||||
!$options['try-common-system-paths'] &&
|
||||
!$options['try-cwebp'] &&
|
||||
!$options['try-discovering-cwebp']
|
||||
) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Configured to neither try pure cwebp command, ' .
|
||||
'nor look for cweb binaries in common system locations and ' .
|
||||
'nor to use one of the supplied precompiled binaries. ' .
|
||||
'But these are the only ways this converter can convert images. No conversion can be made!'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function executeBinary($binary, $commandOptions, $useNice)
|
||||
{
|
||||
//$version = $this->detectVersion($binary);
|
||||
|
||||
// Redirect stderr to same place as stdout with "2>&1"
|
||||
// https://www.brianstorti.com/understanding-shell-script-idiom-redirect/
|
||||
|
||||
$command = ($useNice ? 'nice ' : '') . $binary . ' ' . $commandOptions . ' 2>&1';
|
||||
|
||||
//$logger->logLn('command options:' . $commandOptions);
|
||||
$this->logLn('Trying to convert by executing the following command:');
|
||||
$startExecuteBinaryTime = self::startTimer();
|
||||
;
|
||||
$this->logLn($command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
$this->logExecOutput($output);
|
||||
$this->logTimeSpent($startExecuteBinaryTime, 'Executing cwebp binary took: ');
|
||||
$this->logLn('');
|
||||
/*
|
||||
if ($returnCode == 255) {
|
||||
if (isset($output[0])) {
|
||||
// Could be an error like 'Error! Cannot open output file' or 'Error! ...preset... '
|
||||
$this->logLn(print_r($output[0], true));
|
||||
}
|
||||
}*/
|
||||
//$logger->logLn(self::msgForExitCode($returnCode));
|
||||
return intval($returnCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use "escapeshellarg()" on all arguments in a commandline string of options
|
||||
*
|
||||
* For example, passing '-sharpness 5 -crop 10 10 40 40 -low_memory' will result in:
|
||||
* [
|
||||
* "-sharpness '5'"
|
||||
* "-crop '10' '10' '40' '40'"
|
||||
* "-low_memory"
|
||||
* ]
|
||||
* @param string $commandLineOptions string which can contain multiple commandline options
|
||||
* @return array Array of command options
|
||||
*/
|
||||
private static function escapeShellArgOnCommandLineOptions($commandLineOptions)
|
||||
{
|
||||
if (!ctype_print($commandLineOptions)) {
|
||||
throw new ConversionFailedException(
|
||||
'Non-printable characters are not allowed in the extra command line options'
|
||||
);
|
||||
}
|
||||
|
||||
if (preg_match('#[^a-zA-Z0-9_\s\-]#', $commandLineOptions)) {
|
||||
throw new ConversionFailedException('The extra command line options contains inacceptable characters');
|
||||
}
|
||||
|
||||
$cmdOptions = [];
|
||||
$arr = explode(' -', ' ' . $commandLineOptions);
|
||||
foreach ($arr as $cmdOption) {
|
||||
$pos = strpos($cmdOption, ' ');
|
||||
$cName = '';
|
||||
if (!$pos) {
|
||||
$cName = $cmdOption;
|
||||
if ($cName == '') {
|
||||
continue;
|
||||
}
|
||||
$cmdOptions[] = '-' . $cName;
|
||||
} else {
|
||||
$cName = substr($cmdOption, 0, $pos);
|
||||
$cValues = substr($cmdOption, $pos + 1);
|
||||
$cValuesArr = explode(' ', $cValues);
|
||||
foreach ($cValuesArr as &$cArg) {
|
||||
$cArg = escapeshellarg($cArg);
|
||||
}
|
||||
$cValues = implode(' ', $cValuesArr);
|
||||
$cmdOptions[] = '-' . $cName . ' ' . $cValues;
|
||||
}
|
||||
}
|
||||
return $cmdOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options for a given version of cwebp.
|
||||
*
|
||||
* The "-near_lossless" param is not supported on older versions of cwebp, so skip on those.
|
||||
*
|
||||
* @param string $version Version of cwebp (ie "1.0.3")
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions($version)
|
||||
{
|
||||
|
||||
$this->logLn('Creating command line options for version: ' . $version);
|
||||
|
||||
// we only need two decimal places for version.
|
||||
// convert to number to make it easier to compare
|
||||
$version = preg_match('#^\d+\.\d+#', $version, $matches);
|
||||
$versionNum = 0;
|
||||
if (isset($matches[0])) {
|
||||
$versionNum = floatval($matches[0]);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Could not extract version number from the following version string: ' . $version,
|
||||
'bold'
|
||||
);
|
||||
}
|
||||
|
||||
//$this->logLn('version:' . strval($versionNum));
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$cmdOptions = [];
|
||||
|
||||
// Metadata (all, exif, icc, xmp or none (default))
|
||||
// Comma-separated list of existing metadata to copy from input to output
|
||||
if ($versionNum >= 0.3) {
|
||||
$cmdOptions[] = '-metadata ' . $options['metadata'];
|
||||
} else {
|
||||
$this->logLn('Ignoring metadata option (requires cwebp 0.3)', 'italic');
|
||||
}
|
||||
|
||||
// preset. Appears first in the list as recommended in the docs
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$cmdOptions[] = '-preset ' . escapeshellarg($options['preset']);
|
||||
}
|
||||
}
|
||||
|
||||
// Size
|
||||
$addedSizeOption = false;
|
||||
if (!is_null($options['size-in-percentage'])) {
|
||||
$sizeSource = filesize($this->source);
|
||||
if ($sizeSource !== false) {
|
||||
$targetSize = floor($sizeSource * $options['size-in-percentage'] / 100);
|
||||
$cmdOptions[] = '-size ' . $targetSize;
|
||||
$addedSizeOption = true;
|
||||
}
|
||||
}
|
||||
|
||||
// quality
|
||||
if (!$addedSizeOption) {
|
||||
$cmdOptions[] = '-q ' . $this->getCalculatedQuality();
|
||||
}
|
||||
|
||||
// alpha-quality
|
||||
if ($this->options['alpha-quality'] !== 100) {
|
||||
$cmdOptions[] = '-alpha_q ' . escapeshellarg($this->options['alpha-quality']);
|
||||
}
|
||||
|
||||
// Losless PNG conversion
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
// No need to add -lossless when near-lossless is used (on version >= 0.5)
|
||||
if (($options['near-lossless'] === 100) || ($versionNum < 0.5)) {
|
||||
$cmdOptions[] = '-lossless';
|
||||
}
|
||||
}
|
||||
|
||||
// Near-lossles
|
||||
if ($options['near-lossless'] !== 100) {
|
||||
if ($versionNum < 0.5) {
|
||||
$this->logLn('Ignoring near-lossless option (requires cwebp 0.5)', 'italic');
|
||||
} else {
|
||||
// The "-near_lossless" flag triggers lossless encoding. We don't want that to happen,
|
||||
// we want the "encoding" option to be respected, and we need it to be in order for
|
||||
// encoding=auto to work.
|
||||
// So: Only set when "encoding" is set to "lossless"
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
$cmdOptions[] = '-near_lossless ' . $options['near-lossless'];
|
||||
} else {
|
||||
$this->logLn(
|
||||
'The near-lossless option ignored for lossy'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Autofilter
|
||||
if ($options['auto-filter'] === true) {
|
||||
$cmdOptions[] = '-af';
|
||||
}
|
||||
|
||||
// SharpYUV
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
if ($versionNum >= 0.6) { // #284
|
||||
$cmdOptions[] = '-sharp_yuv';
|
||||
} else {
|
||||
$this->logLn('Ignoring sharp-yuv option (requires cwebp 0.6)', 'italic');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Built-in method option
|
||||
$cmdOptions[] = '-m ' . strval($options['method']);
|
||||
|
||||
// Built-in low memory option
|
||||
if ($options['low-memory']) {
|
||||
$cmdOptions[] = '-low_memory';
|
||||
}
|
||||
|
||||
// command-line-options
|
||||
if ($options['command-line-options']) {
|
||||
/*
|
||||
In some years, we can use the splat instead (requires PHP 5.6)
|
||||
array_push(
|
||||
$cmdOptions,
|
||||
...self::escapeShellArgOnCommandLineOptions($options['command-line-options'])
|
||||
);
|
||||
*/
|
||||
foreach (self::escapeShellArgOnCommandLineOptions($options['command-line-options']) as $cmdLineOption) {
|
||||
array_push($cmdOptions, $cmdLineOption);
|
||||
}
|
||||
}
|
||||
|
||||
// Source file
|
||||
$cmdOptions[] = escapeshellarg($this->source);
|
||||
|
||||
// Output
|
||||
$cmdOptions[] = '-o ' . escapeshellarg($this->destination);
|
||||
|
||||
$commandOptions = implode(' ', $cmdOptions);
|
||||
//$this->logLn('command line options:' . $commandOptions);
|
||||
|
||||
return $commandOptions;
|
||||
}
|
||||
|
||||
private function checkHashForSuppliedBinary($binaryFile, $hash)
|
||||
{
|
||||
// File exists, now generate its hash
|
||||
// hash_file() is normally available, but it is not always
|
||||
// - https://stackoverflow.com/questions/17382712/php-5-3-20-undefined-function-hash
|
||||
// If available, validate that hash is correct.
|
||||
|
||||
if (function_exists('hash_file')) {
|
||||
$this->logLn(
|
||||
'Checking checksum for supplied binary: ' . $binaryFile
|
||||
);
|
||||
$startHashCheckTime = self::startTimer();
|
||||
|
||||
$binaryHash = hash_file('sha256', $binaryFile);
|
||||
|
||||
if ($binaryHash != $hash) {
|
||||
$this->logLn(
|
||||
'Binary checksum of supplied binary is invalid! ' .
|
||||
'Did you transfer with FTP, but not in binary mode? ' .
|
||||
'File:' . $binaryFile . '. ' .
|
||||
'Expected checksum: ' . $hash . '. ' .
|
||||
'Actual checksum:' . $binaryHash . '.',
|
||||
'bold'
|
||||
);
|
||||
return false;
|
||||
;
|
||||
}
|
||||
|
||||
$this->logTimeSpent($startHashCheckTime, 'Checksum test took: ');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supplied binary info for current OS.
|
||||
* paths are made absolute and checked. Missing are removed
|
||||
*
|
||||
* @return array Two arrays.
|
||||
* First array: array of files (absolute paths)
|
||||
* Second array: array of info objects (absolute path, hash and version)
|
||||
*/
|
||||
private function getSuppliedBinaryInfoForCurrentOS()
|
||||
{
|
||||
$this->log('Checking if we have a supplied precompiled binary for your OS (' . PHP_OS . ')... ');
|
||||
|
||||
// Try supplied binary (if available for OS, and hash is correct)
|
||||
$options = $this->options;
|
||||
if (!isset(self::$suppliedBinariesInfo[PHP_OS])) {
|
||||
$this->logLn('No we dont - not for that OS');
|
||||
return [];
|
||||
}
|
||||
|
||||
$filesFound = [];
|
||||
$info = [];
|
||||
$files = self::$suppliedBinariesInfo[PHP_OS];
|
||||
if (count($files) == 1) {
|
||||
$this->logLn('We do.');
|
||||
} else {
|
||||
$this->logLn('We do. We in fact have ' . count($files));
|
||||
}
|
||||
|
||||
$skipThese = explode(',', $this->options['skip-these-precompiled-binaries']);
|
||||
|
||||
//$this->logLn('However, skipping' . print_r($skipThese, true));
|
||||
|
||||
foreach ($files as $i => list($file, $hash, $version)) {
|
||||
//$file = $info[0];
|
||||
//$hash = $info[1];
|
||||
|
||||
$binaryFile = __DIR__ . '/' . $options['rel-path-to-precompiled-binaries'] . '/' . $file;
|
||||
|
||||
// Replace "/./" with "/" in path (we could alternatively use realpath)
|
||||
//$binaryFile = preg_replace('#\/\.\/#', '/', $binaryFile);
|
||||
// The file should exist, but may have been removed manually.
|
||||
/*
|
||||
if (!file_exists($binaryFile)) {
|
||||
$this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
|
||||
return false;
|
||||
}*/
|
||||
if (in_array($file, $skipThese)) {
|
||||
$this->logLn('Skipped: ' . $file . ' (was told to in the "skip-these-precompiled-binaries" option)');
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$realPathResult = realpath($binaryFile);
|
||||
if ($realPathResult === false) {
|
||||
$this->logLn('Supplied binary not found! It ought to be here:' . $binaryFile, 'italic');
|
||||
continue;
|
||||
}
|
||||
$binaryFile = $realPathResult;
|
||||
$filesFound[] = $realPathResult;
|
||||
$info[] = [$realPathResult, $hash, $version, $file];
|
||||
}
|
||||
return [$filesFound, $info];
|
||||
}
|
||||
|
||||
private function who()
|
||||
{
|
||||
ExecWithFallback::exec('whoami 2>&1', $whoOutput, $whoReturnCode);
|
||||
if (($whoReturnCode == 0) && (isset($whoOutput[0]))) {
|
||||
return 'user: "' . $whoOutput[0] . '"';
|
||||
} else {
|
||||
return 'the user that the command was run with';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the version of a cwebp binary.
|
||||
*
|
||||
* @param string $binary The binary to detect version for (path to cwebp or simply "cwebp")
|
||||
*
|
||||
* @return string|int Version string (ie "1.0.2") OR return code, in case of failure
|
||||
*/
|
||||
private function detectVersion($binary)
|
||||
{
|
||||
$command = $binary . ' -version 2>&1';
|
||||
$this->log('- Executing: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
if ($returnCode == 0) {
|
||||
if (isset($output[0])) {
|
||||
$this->logLn('. Result: version: *' . $output[0] . '*');
|
||||
return $output[0];
|
||||
}
|
||||
} else {
|
||||
$this->log('. Result: ');
|
||||
if ($returnCode == 127) {
|
||||
$this->logLn(
|
||||
'*Exec failed* (the cwebp binary was not found at path: ' . $binary .
|
||||
', or it had missing library dependencies)'
|
||||
);
|
||||
} else {
|
||||
if ($returnCode == 126) {
|
||||
$this->logLn(
|
||||
'*Exec failed*. ' .
|
||||
'Permission denied (' . $this->who() . ' does not have permission to execute that binary)'
|
||||
);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'*Exec failed* (return code: ' . $returnCode . ')'
|
||||
);
|
||||
$this->logExecOutput($output);
|
||||
}
|
||||
}
|
||||
return $returnCode;
|
||||
}
|
||||
return ''; // Will not happen. Just so phpstan doesn't complain
|
||||
}
|
||||
|
||||
/**
|
||||
* Check versions for an array of binaries.
|
||||
*
|
||||
* @param array $binaries array of binaries to detect the version of
|
||||
*
|
||||
* @return array the "detected" key holds working binaries and their version numbers, the
|
||||
* the "failed" key holds failed binaries and their error codes.
|
||||
*/
|
||||
private function detectVersions($binaries)
|
||||
{
|
||||
$binariesWithVersions = [];
|
||||
$binariesWithFailCodes = [];
|
||||
|
||||
foreach ($binaries as $binary) {
|
||||
$versionStringOrFailCode = $this->detectVersion($binary);
|
||||
// $this->logLn($binary . ': ' . $versionString);
|
||||
if (gettype($versionStringOrFailCode) == 'string') {
|
||||
$binariesWithVersions[$binary] = $versionStringOrFailCode;
|
||||
} else {
|
||||
$binariesWithFailCodes[$binary] = $versionStringOrFailCode;
|
||||
}
|
||||
}
|
||||
return ['detected' => $binariesWithVersions, 'failed' => $binariesWithFailCodes];
|
||||
}
|
||||
|
||||
private function logBinariesFound($binaries, $startTime)
|
||||
{
|
||||
if (count($binaries) == 0) {
|
||||
$this->logLn('Found 0 binaries' . self::getTimeStr($startTime));
|
||||
} else {
|
||||
$this->logLn('Found ' . count($binaries) . ' binaries' . self::getTimeStr($startTime));
|
||||
foreach ($binaries as $binary) {
|
||||
$this->logLn('- ' . $binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function logDiscoverAction($optionName, $description)
|
||||
{
|
||||
if ($this->options[$optionName]) {
|
||||
$this->logLn(
|
||||
'Discovering binaries ' . $description . ' ' .
|
||||
'(to skip this step, disable the "' . $optionName . '" option)'
|
||||
);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Skipped discovering binaries ' . $description . ' ' .
|
||||
'(enable "' . $optionName . '" if you do not want to skip that step)'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function startTimer()
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
return microtime(true);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static function readTimer($startTime)
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
$endTime = microtime(true);
|
||||
$seconds = ($endTime - $startTime);
|
||||
return round(($seconds * 1000));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getTimeStr($startTime, $pre = ' (spent ', $post = ')')
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
$ms = self::readTimer($startTime);
|
||||
return $pre . $ms . ' ms' . $post;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function logTimeSpent($startTime, $pre = 'Spent: ')
|
||||
{
|
||||
if (function_exists('microtime')) {
|
||||
$ms = self::readTimer($startTime);
|
||||
$this->logLn($pre . $ms . ' ms');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Two arrays (in an array).
|
||||
* First array: binaries found,
|
||||
* Second array: supplied binaries info for current OS
|
||||
*/
|
||||
private function discoverCwebpBinaries()
|
||||
{
|
||||
$this->logLn(
|
||||
'Looking for cwebp binaries.'
|
||||
);
|
||||
|
||||
$startDiscoveryTime = self::startTimer();
|
||||
|
||||
$binaries = [];
|
||||
|
||||
if (defined('WEBPCONVERT_CWEBP_PATH')) {
|
||||
$this->logLn('WEBPCONVERT_CWEBP_PATH was defined, so using that path and ignoring any other');
|
||||
return [[constant('WEBPCONVERT_CWEBP_PATH')],[[], []]];
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_CWEBP_PATH'))) {
|
||||
$this->logLn(
|
||||
'WEBPCONVERT_CWEBP_PATH environment variable was set, so using that path and ignoring any other'
|
||||
);
|
||||
return [[getenv('WEBPCONVERT_CWEBP_PATH')],[[], []]];
|
||||
}
|
||||
|
||||
if ($this->options['try-cwebp']) {
|
||||
$startTime = self::startTimer();
|
||||
$this->logLn(
|
||||
'Discovering if a plain cwebp call works (to skip this step, disable the "try-cwebp" option)'
|
||||
);
|
||||
$result = $this->detectVersion('cwebp');
|
||||
if (gettype($result) == 'string') {
|
||||
$this->logLn('We could get the version, so yes, a plain cwebp call works ' .
|
||||
'(spent ' . self::readTimer($startTime) . ' ms)');
|
||||
$binaries[] = 'cwebp';
|
||||
} else {
|
||||
$this->logLn('Nope a plain cwebp call does not work' . self::getTimeStr($startTime));
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Skipped discovering if a plain cwebp call works' .
|
||||
' (enable the "try-cwebp" option if you do not want to skip that step)'
|
||||
);
|
||||
}
|
||||
|
||||
// try-discovering-cwebp
|
||||
$startTime = self::startTimer();
|
||||
$this->logDiscoverAction('try-discovering-cwebp', 'using "which -a cwebp" command.');
|
||||
if ($this->options['try-discovering-cwebp']) {
|
||||
$moreBinaries = LocateBinaries::locateInstalledBinaries('cwebp');
|
||||
$this->logBinariesFound($moreBinaries, $startTime);
|
||||
$binaries = array_merge($binaries, $moreBinaries);
|
||||
}
|
||||
|
||||
// 'try-common-system-paths'
|
||||
$startTime = self::startTimer();
|
||||
$this->logDiscoverAction('try-common-system-paths', 'by peeking in common system paths');
|
||||
if ($this->options['try-common-system-paths']) {
|
||||
$moreBinaries = LocateBinaries::locateInCommonSystemPaths('cwebp');
|
||||
$this->logBinariesFound($moreBinaries, $startTime);
|
||||
$binaries = array_merge($binaries, $moreBinaries);
|
||||
}
|
||||
|
||||
// try-supplied-binary-for-os
|
||||
$suppliedBinariesInfo = [[], []];
|
||||
$startTime = self::startTimer();
|
||||
$this->logDiscoverAction('try-supplied-binary-for-os', 'which are distributed with the webp-convert library');
|
||||
if ($this->options['try-supplied-binary-for-os']) {
|
||||
$suppliedBinariesInfo = $this->getSuppliedBinaryInfoForCurrentOS();
|
||||
$moreBinaries = $suppliedBinariesInfo[0];
|
||||
$this->logBinariesFound($moreBinaries, $startTime);
|
||||
//$binaries = array_merge($binaries, $moreBinaries);
|
||||
}
|
||||
|
||||
$this->logTimeSpent($startDiscoveryTime, 'Discovering cwebp binaries took: ');
|
||||
$this->logLn('');
|
||||
|
||||
return [array_values(array_unique($binaries)), $suppliedBinariesInfo];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try executing a cwebp binary (or command, like: "cwebp")
|
||||
*
|
||||
* @param string $binary
|
||||
* @param string $version Version of cwebp (ie "1.0.3")
|
||||
* @param boolean $useNice Whether to use "nice" command or not
|
||||
*
|
||||
* @return boolean success or not.
|
||||
*/
|
||||
private function tryCwebpBinary($binary, $version, $useNice)
|
||||
{
|
||||
|
||||
//$this->logLn('Trying binary: ' . $binary);
|
||||
$commandOptions = $this->createCommandLineOptions($version);
|
||||
|
||||
$returnCode = $this->executeBinary($binary, $commandOptions, $useNice);
|
||||
if ($returnCode == 0) {
|
||||
// It has happened that even with return code 0, there was no file at destination.
|
||||
if (!file_exists($this->destination)) {
|
||||
$this->logLn('executing cweb returned success code - but no file was found at destination!');
|
||||
return false;
|
||||
} else {
|
||||
$this->logLn('Success');
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Exec failed (return code: ' . $returnCode . ')'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for composing an error message when no converters are working.
|
||||
*
|
||||
* @param array $versions The array which we get from calling ::detectVersions($binaries)
|
||||
* @return string An informative and to the point error message.
|
||||
*/
|
||||
private function composeMeaningfullErrorMessageNoVersionsWorking($versions)
|
||||
{
|
||||
// TODO: Take "supplied" into account
|
||||
|
||||
// PS: array_values() is used to reindex
|
||||
$uniqueFailCodes = array_values(array_unique(array_values($versions['failed'])));
|
||||
$justOne = (count($versions['failed']) == 1);
|
||||
|
||||
if (count($uniqueFailCodes) == 1) {
|
||||
if ($uniqueFailCodes[0] == 127) {
|
||||
return 'No cwebp binaries located. Check the conversion log for details.';
|
||||
}
|
||||
}
|
||||
// If there are more failures than 127, the 127 failures are unintesting.
|
||||
// It is to be expected that some of the common system paths does not contain a cwebp.
|
||||
$uniqueFailCodesBesides127 = array_values(array_diff($uniqueFailCodes, [127]));
|
||||
|
||||
if (count($uniqueFailCodesBesides127) == 1) {
|
||||
if ($uniqueFailCodesBesides127[0] == 126) {
|
||||
return 'No cwebp binaries could be executed (permission denied for ' . $this->who() . ').';
|
||||
}
|
||||
}
|
||||
|
||||
$errorMsg = '';
|
||||
if ($justOne) {
|
||||
$errorMsg .= 'The cwebp file found cannot be can be executed ';
|
||||
} else {
|
||||
$errorMsg .= 'None of the cwebp files can be executed ';
|
||||
}
|
||||
if (count($uniqueFailCodesBesides127) == 1) {
|
||||
$errorMsg .= '(failure code: ' . $uniqueFailCodesBesides127[0] . ')';
|
||||
} else {
|
||||
$errorMsg .= '(failure codes: ' . implode(', ', $uniqueFailCodesBesides127) . ')';
|
||||
}
|
||||
return $errorMsg;
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
list($foundBinaries, $suppliedBinariesInfo) = $this->discoverCwebpBinaries();
|
||||
$suppliedBinaries = $suppliedBinariesInfo[0];
|
||||
$allBinaries = array_merge($foundBinaries, $suppliedBinaries);
|
||||
|
||||
//$binaries = $this->discoverCwebpBinaries();
|
||||
if (count($allBinaries) == 0) {
|
||||
$this->logLn('No cwebp binaries found!');
|
||||
|
||||
$discoverOptions = [
|
||||
'try-supplied-binary-for-os',
|
||||
'try-common-system-paths',
|
||||
'try-cwebp',
|
||||
'try-discovering-cwebp'
|
||||
];
|
||||
$disabledDiscoverOptions = [];
|
||||
foreach ($discoverOptions as $discoverOption) {
|
||||
if (!$this->options[$discoverOption]) {
|
||||
$disabledDiscoverOptions[] = $discoverOption;
|
||||
}
|
||||
}
|
||||
if (count($disabledDiscoverOptions) == 0) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'No cwebp binaries found.'
|
||||
);
|
||||
} else {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'No cwebp binaries found. Try enabling the "' .
|
||||
implode('" option or the "', $disabledDiscoverOptions) . '" option.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$detectedVersions = [];
|
||||
if (count($foundBinaries) > 0) {
|
||||
$this->logLn(
|
||||
'Detecting versions of the cwebp binaries found' .
|
||||
(count($suppliedBinaries) > 0 ? ' (except supplied binaries)' : '.')
|
||||
);
|
||||
$startDetectionTime = self::startTimer();
|
||||
$versions = $this->detectVersions($foundBinaries);
|
||||
$detectedVersions = $versions['detected'];
|
||||
|
||||
$this->logTimeSpent($startDetectionTime, 'Detecting versions took: ');
|
||||
}
|
||||
|
||||
//$suppliedVersions = [];
|
||||
$suppliedBinariesHash = [];
|
||||
$suppliedBinariesFilename = [];
|
||||
|
||||
$binaryVersions = $detectedVersions;
|
||||
foreach ($suppliedBinariesInfo[1] as list($path, $hash, $version, $filename)) {
|
||||
$binaryVersions[$path] = $version;
|
||||
$suppliedBinariesHash[$path] = $hash;
|
||||
$suppliedBinariesFilename[$path] = $filename;
|
||||
}
|
||||
|
||||
//$binaryVersions = array_merge($detectedVersions, $suppliedBinariesInfo);
|
||||
|
||||
// TODO: reimplement
|
||||
/*
|
||||
$versions['supplied'] = $suppliedBinariesInfo;
|
||||
|
||||
$binaryVersions = $versions['detected'];
|
||||
if ((count($binaryVersions) == 0) && (count($suppliedBinaries) == 0)) {
|
||||
// No working cwebp binaries found, no supplied binaries found
|
||||
|
||||
throw new SystemRequirementsNotMetException(
|
||||
$this->composeMeaningfullErrorMessageNoVersionsWorking($versions)
|
||||
);
|
||||
}*/
|
||||
|
||||
// Sort binaries so those with highest numbers comes first
|
||||
arsort($binaryVersions);
|
||||
$this->logLn(
|
||||
'Binaries ordered by version number.'
|
||||
);
|
||||
foreach ($binaryVersions as $binary => $version) {
|
||||
$this->logLn('- ' . $binary . ': (version: ' . $version . ')');
|
||||
}
|
||||
|
||||
// Execute!
|
||||
$this->logLn(
|
||||
'Starting conversion, using the first of these. If that should fail, ' .
|
||||
'the next will be tried and so on.'
|
||||
);
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
|
||||
$success = false;
|
||||
foreach ($binaryVersions as $binary => $version) {
|
||||
if (isset($suppliedBinariesHash[$binary])) {
|
||||
if (!$this->checkHashForSuppliedBinary($binary, $suppliedBinariesHash[$binary])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($this->tryCwebpBinary($binary, $version, $useNice)) {
|
||||
$success = true;
|
||||
break;
|
||||
} else {
|
||||
if (isset($suppliedBinariesFilename[$binary])) {
|
||||
$this->logLn(
|
||||
'Note: You can prevent trying this precompiled binary, by setting the ' .
|
||||
'"skip-these-precompiled-binaries" option to "' . $suppliedBinariesFilename[$binary] . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cwebp sets file permissions to 664 but instead ..
|
||||
// .. $this->source file permissions should be used
|
||||
|
||||
if ($success) {
|
||||
$fileStatistics = stat($this->source);
|
||||
if ($fileStatistics !== false) {
|
||||
// Apply same permissions as source file, but strip off the executable bits
|
||||
$permissions = $fileStatistics['mode'] & 0000666;
|
||||
chmod($this->destination, $permissions);
|
||||
}
|
||||
} else {
|
||||
throw new SystemRequirementsNotMetException('Failed converting. Check the conversion log for details.');
|
||||
}
|
||||
}
|
||||
}
|
||||
393
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Ewww.php
vendored
Normal file
393
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Ewww.php
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\SensitiveStringOption;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Convert images to webp using ewww cloud service.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Ewww extends AbstractConverter
|
||||
{
|
||||
use CloudConverterTrait;
|
||||
use CurlTrait;
|
||||
|
||||
/** @var array|null Array of invalid or exceeded api keys discovered during conversions (during the request) */
|
||||
public static $nonFunctionalApiKeysDiscoveredDuringConversion;
|
||||
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
['api-key', 'string', [
|
||||
'title' => 'Ewww API key',
|
||||
'description' => 'ewww API key. ' .
|
||||
'If you choose "auto", webp-convert will ' .
|
||||
'convert to both lossy and lossless and pick the smallest result',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
]
|
||||
]],
|
||||
['check-key-status-before-converting', 'boolean', [
|
||||
'title' => 'Check key status before converting',
|
||||
'description' =>
|
||||
'If enabled, the api key will be validated (relative inexpensive) before trying ' .
|
||||
'to convert. For automatic conversions, you should enable it. Otherwise you run the ' .
|
||||
'risk that the same files will be uploaded to ewww cloud service over and over again, ' .
|
||||
'in case the key has expired. For manually triggered conversions, you can safely disable ' .
|
||||
'the option.',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
]
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'encoding',
|
||||
'low-memory',
|
||||
'method',
|
||||
'near-lossless',
|
||||
'preset',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get api key from options or environment variable
|
||||
*
|
||||
* @return string|false api key or false if none is set
|
||||
*/
|
||||
private function getKey()
|
||||
{
|
||||
if (!empty($this->options['api-key'])) {
|
||||
return $this->options['api-key'];
|
||||
}
|
||||
if (defined('WEBPCONVERT_EWWW_API_KEY')) {
|
||||
return constant('WEBPCONVERT_EWWW_API_KEY');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_EWWW_API_KEY'))) {
|
||||
return getenv('WEBPCONVERT_EWWW_API_KEY');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check operationality of Ewww converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met (curl)
|
||||
* @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
|
||||
$apiKey = $this->getKey();
|
||||
|
||||
if ($apiKey === false) {
|
||||
if (isset($this->options['key'])) {
|
||||
throw new InvalidApiKeyException(
|
||||
'The "key" option has been renamed to "api-key" in webp-convert 2.0. ' .
|
||||
'You must change the configuration accordingly.'
|
||||
);
|
||||
}
|
||||
|
||||
throw new InvalidApiKeyException('Missing API key.');
|
||||
}
|
||||
|
||||
if (strlen($apiKey) < 20) {
|
||||
throw new InvalidApiKeyException(
|
||||
'Api key is invalid. Api keys are supposed to be 32 characters long - ' .
|
||||
'the provided api key is much shorter'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for curl requirements
|
||||
$this->checkOperationalityForCurlTrait();
|
||||
|
||||
if ($this->options['check-key-status-before-converting']) {
|
||||
$keyStatus = self::getKeyStatus($apiKey);
|
||||
switch ($keyStatus) {
|
||||
case 'great':
|
||||
break;
|
||||
case 'exceeded':
|
||||
throw new ConverterNotOperationalException('Quota has exceeded');
|
||||
//break;
|
||||
case 'invalid':
|
||||
throw new InvalidApiKeyException('Api key is invalid');
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public function checkConvertability()
|
||||
{
|
||||
// check upload limits
|
||||
$this->checkConvertabilityCloudConverterTrait();
|
||||
}
|
||||
*/
|
||||
|
||||
// Although this method is public, do not call directly.
|
||||
// You should rather call the static convert() function, defined in AbstractConverter, which
|
||||
// takes care of preparing stuff before calling doConvert, and validating after.
|
||||
protected function doActualConvert()
|
||||
{
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$ch = self::initCurl();
|
||||
|
||||
//$this->logLn('api key:' . $this->getKey());
|
||||
|
||||
$postData = [
|
||||
'api_key' => $this->getKey(),
|
||||
'webp' => '1',
|
||||
'file' => curl_file_create($this->source),
|
||||
'quality' => $this->getCalculatedQuality(),
|
||||
'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
|
||||
];
|
||||
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'User-Agent: WebPConvert',
|
||||
'Accept: image/*'
|
||||
],
|
||||
CURLOPT_POSTFIELDS => $postData,
|
||||
CURLOPT_BINARYTRANSFER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false
|
||||
]
|
||||
);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
throw new ConversionFailedException(curl_error($ch));
|
||||
}
|
||||
|
||||
// The API does not always return images.
|
||||
// For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
|
||||
// Messages has a http content type of ie 'text/html; charset=UTF-8
|
||||
// Images has application/octet-stream.
|
||||
// So verify that we got an image back.
|
||||
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
||||
//echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
curl_close($ch);
|
||||
|
||||
/*
|
||||
For bogus or expired key it returns: {"error":"invalid","t":"exceeded"}
|
||||
For exceeded key it returns: {"error":"exceeded"}
|
||||
*/
|
||||
$responseObj = json_decode($response);
|
||||
if (isset($responseObj->error)) {
|
||||
$this->logLn('We received the following error response: ' . $responseObj->error);
|
||||
$this->logLn('Complete response: ' . json_encode($responseObj));
|
||||
|
||||
// Store the invalid key in array so it can be received once the Stack is completed
|
||||
// (even when stack succeeds)
|
||||
if (!isset(self::$nonFunctionalApiKeysDiscoveredDuringConversion)) {
|
||||
self::$nonFunctionalApiKeysDiscoveredDuringConversion = [];
|
||||
}
|
||||
if (!in_array($options['api-key'], self::$nonFunctionalApiKeysDiscoveredDuringConversion)) {
|
||||
self::$nonFunctionalApiKeysDiscoveredDuringConversion[] = $options['api-key'];
|
||||
}
|
||||
if ($responseObj->error == "invalid") {
|
||||
throw new InvalidApiKeyException('The api key is invalid (or expired)');
|
||||
} else {
|
||||
throw new InvalidApiKeyException('The quota is exceeded for the api-key');
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionFailedException(
|
||||
'ewww api did not return an image. It could be that the key is invalid. Response: '
|
||||
. $response
|
||||
);
|
||||
}
|
||||
|
||||
// Not sure this can happen. So just in case
|
||||
if ($response == '') {
|
||||
throw new ConversionFailedException('ewww api did not return anything');
|
||||
}
|
||||
|
||||
$success = file_put_contents($this->destination, $response);
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException('Error saving file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep subscription alive by optimizing a jpeg
|
||||
* (ewww closes accounts after 6 months of inactivity - and webp conversions seems not to be counted? )
|
||||
*/
|
||||
public static function keepSubscriptionAlive($source, $key)
|
||||
{
|
||||
try {
|
||||
$ch = curl_init();
|
||||
} catch (\Exception $e) {
|
||||
return 'curl is not installed';
|
||||
}
|
||||
if ($ch === false) {
|
||||
return 'curl could not be initialized';
|
||||
}
|
||||
curl_setopt_array(
|
||||
$ch,
|
||||
[
|
||||
CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'User-Agent: WebPConvert',
|
||||
'Accept: image/*'
|
||||
],
|
||||
CURLOPT_POSTFIELDS => [
|
||||
'api_key' => $key,
|
||||
'webp' => '0',
|
||||
'file' => curl_file_create($source),
|
||||
'domain' => $_SERVER['HTTP_HOST'],
|
||||
'quality' => 60,
|
||||
'metadata' => 0
|
||||
],
|
||||
CURLOPT_BINARYTRANSFER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false
|
||||
]
|
||||
);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
return 'curl error' . curl_error($ch);
|
||||
}
|
||||
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
||||
curl_close($ch);
|
||||
|
||||
/* May return this: {"error":"invalid","t":"exceeded"} */
|
||||
$responseObj = json_decode($response);
|
||||
if (isset($responseObj->error)) {
|
||||
return 'The key is invalid';
|
||||
}
|
||||
|
||||
return 'ewww api did not return an image. It could be that the key is invalid. Response: ' . $response;
|
||||
}
|
||||
|
||||
// Not sure this can happen. So just in case
|
||||
if ($response == '') {
|
||||
return 'ewww api did not return anything';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
public static function blacklistKey($key)
|
||||
{
|
||||
}
|
||||
|
||||
public static function isKeyBlacklisted($key)
|
||||
{
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Return "great", "exceeded" or "invalid"
|
||||
*/
|
||||
public static function getKeyStatus($key)
|
||||
{
|
||||
$ch = self::initCurl();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||||
'api_key' => $key
|
||||
]);
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'WebPConvert');
|
||||
|
||||
$response = curl_exec($ch);
|
||||
// echo $response;
|
||||
if (curl_errno($ch)) {
|
||||
throw new \Exception(curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
// Possible responses:
|
||||
// “great” = verification successful
|
||||
// “exceeded” = indicates a valid key with no remaining image credits.
|
||||
// an empty response indicates that the key is not valid
|
||||
|
||||
if ($response == '') {
|
||||
return 'invalid';
|
||||
}
|
||||
$responseObj = json_decode($response);
|
||||
if (isset($responseObj->error)) {
|
||||
if ($responseObj->error == 'invalid') {
|
||||
return 'invalid';
|
||||
} else {
|
||||
if ($responseObj->error == 'bye invalid') {
|
||||
return 'invalid';
|
||||
} else {
|
||||
throw new \Exception('Ewww returned unexpected error: ' . $response);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($responseObj->status)) {
|
||||
throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
|
||||
}
|
||||
switch ($responseObj->status) {
|
||||
case 'great':
|
||||
case 'exceeded':
|
||||
return $responseObj->status;
|
||||
}
|
||||
throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
|
||||
}
|
||||
|
||||
public static function isWorkingKey($key)
|
||||
{
|
||||
return (self::getKeyStatus($key) == 'great');
|
||||
}
|
||||
|
||||
public static function isValidKey($key)
|
||||
{
|
||||
return (self::getKeyStatus($key) != 'invalid');
|
||||
}
|
||||
|
||||
public static function getQuota($key)
|
||||
{
|
||||
$ch = self::initCurl();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||||
'api_key' => $key
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'WebPConvert');
|
||||
|
||||
$response = curl_exec($ch);
|
||||
return $response; // ie -830 23. Seems to return empty for invalid keys
|
||||
// or empty
|
||||
//echo $response;
|
||||
}
|
||||
}
|
||||
178
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/FFMpeg.php
vendored
Normal file
178
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/FFMpeg.php
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling imagemagick binary.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class FFMpeg extends AbstractConverter
|
||||
{
|
||||
use ExecTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'low-memory',
|
||||
'metadata',
|
||||
'near-lossless',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption()
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPath()
|
||||
{
|
||||
if (defined('WEBPCONVERT_FFMPEG_PATH')) {
|
||||
return constant('WEBPCONVERT_FFMPEG_PATH');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_FFMPEG_PATH'))) {
|
||||
return getenv('WEBPCONVERT_FFMPEG_PATH');
|
||||
}
|
||||
return 'ffmpeg';
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
return ($returnCode == 0);
|
||||
}
|
||||
|
||||
// Check if webp delegate is installed
|
||||
public function isWebPDelegateInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('# --enable-libwebp#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
if (!$this->isInstalled()) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'ffmpeg is not installed (cannot execute: "' . $this->getPath() . '")'
|
||||
);
|
||||
}
|
||||
if (!$this->isWebPDelegateInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('ffmpeg was compiled without libwebp');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions()
|
||||
{
|
||||
// PS: Available webp options for ffmpeg are documented here:
|
||||
// https://www.ffmpeg.org/ffmpeg-codecs.html#libwebp
|
||||
|
||||
$commandArguments = [];
|
||||
|
||||
$commandArguments[] = '-i';
|
||||
$commandArguments[] = escapeshellarg($this->source);
|
||||
|
||||
// preset. Appears first in the list as recommended in the cwebp docs
|
||||
if (!is_null($this->options['preset'])) {
|
||||
if ($this->options['preset'] != 'none') {
|
||||
$commandArguments[] = '-preset ' . $this->options['preset'];
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite existing files?, yes!
|
||||
$commandArguments[] = '-y';
|
||||
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// quality:auto was specified, but could not be determined.
|
||||
// we cannot apply the max-quality logic, but we can provide auto quality
|
||||
// simply by not specifying the quality option.
|
||||
} else {
|
||||
$commandArguments[] = '-qscale ' . escapeshellarg($this->getCalculatedQuality());
|
||||
}
|
||||
if ($this->options['encoding'] == 'lossless') {
|
||||
$commandArguments[] = '-lossless 1';
|
||||
} else {
|
||||
$commandArguments[] = '-lossless 0';
|
||||
}
|
||||
|
||||
if ($this->options['metadata'] == 'none') {
|
||||
// Unfortunately there seems to be no easy solution available for removing all metadata.
|
||||
}
|
||||
|
||||
// compression_level maps to method, according to https://www.ffmpeg.org/ffmpeg-codecs.html#libwebp
|
||||
$commandArguments[] = '-compression_level ' . $this->options['method'];
|
||||
|
||||
$commandArguments[] = escapeshellarg($this->destination);
|
||||
|
||||
|
||||
return implode(' ', $commandArguments);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
//$this->logLn($this->getVersion());
|
||||
|
||||
$command = $this->getPath() . ' ' . $this->createCommandLineOptions() . ' 2>&1';
|
||||
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
if ($useNice) {
|
||||
$command = 'nice ' . $command;
|
||||
}
|
||||
$this->logLn('Executing command: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
$this->logExecOutput($output);
|
||||
if ($returnCode == 0) {
|
||||
$this->logLn('success');
|
||||
} else {
|
||||
$this->logLn('return code: ' . $returnCode);
|
||||
}
|
||||
|
||||
if ($returnCode == 127) {
|
||||
throw new SystemRequirementsNotMetException('ffmpeg is not installed');
|
||||
}
|
||||
if ($returnCode != 0) {
|
||||
throw new SystemRequirementsNotMetException('The exec() call failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
536
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Gd.php
vendored
Normal file
536
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Gd.php
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
/**
|
||||
* Convert images to webp using gd extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Gd extends AbstractConverter
|
||||
{
|
||||
public function supportsLossless()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'encoding',
|
||||
'low-memory',
|
||||
'metadata',
|
||||
'method',
|
||||
'near-lossless',
|
||||
'preset',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
private $errorMessageWhileCreating = '';
|
||||
private $errorNumberWhileCreating;
|
||||
|
||||
/**
|
||||
* Check (general) operationality of Gd converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new SystemRequirementsNotMetException('Required Gd extension is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('imagewebp')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd has been compiled without webp support.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists('imagepalettetotruecolor')) {
|
||||
if (!self::functionsExist([
|
||||
'imagecreatetruecolor', 'imagealphablending', 'imagecolorallocatealpha',
|
||||
'imagefilledrectangle', 'imagecopy', 'imagedestroy', 'imagesx', 'imagesy'
|
||||
])) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd cannot convert palette color images to RGB. ' .
|
||||
'Even though it would be possible to convert RGB images to webp with Gd, ' .
|
||||
'we refuse to do it. A partial working converter causes more trouble than ' .
|
||||
'a non-working. To make this converter work, you need the imagepalettetotruecolor() ' .
|
||||
'function to be enabled on your system.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Gd has been compiled without support for image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
if (!function_exists('imagecreatefrompng')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd has been compiled without PNG support and can therefore not convert this PNG image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
if (!function_exists('imagecreatefromjpeg')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gd has been compiled without Jpeg support and can therefore not convert this jpeg image.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if all functions exists.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private static function functionsExist($functionNamesArr)
|
||||
{
|
||||
foreach ($functionNamesArr as $functionName) {
|
||||
if (!function_exists($functionName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert image pallette to true color on older systems that does not have imagepalettetotruecolor().
|
||||
*
|
||||
* The aim is to function as imagepalettetotruecolor, but for older systems.
|
||||
* So, if the image is already rgb, nothing will be done, and true will be returned
|
||||
* PS: Got the workaround here: https://secure.php.net/manual/en/function.imagepalettetotruecolor.php
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
|
||||
* otherwise FALSE is returned.
|
||||
*/
|
||||
private function makeTrueColorUsingWorkaround(&$image)
|
||||
{
|
||||
//return $this->makeTrueColor($image);
|
||||
/*
|
||||
if (function_exists('imageistruecolor') && imageistruecolor($image)) {
|
||||
return true;
|
||||
}*/
|
||||
if (self::functionsExist(['imagecreatetruecolor', 'imagealphablending', 'imagecolorallocatealpha',
|
||||
'imagefilledrectangle', 'imagecopy', 'imagedestroy', 'imagesx', 'imagesy'])) {
|
||||
$dst = imagecreatetruecolor(imagesx($image), imagesy($image));
|
||||
|
||||
if ($dst === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = false;
|
||||
|
||||
//prevent blending with default black
|
||||
if (imagealphablending($dst, false) !== false) {
|
||||
//change the RGB values if you need, but leave alpha at 127
|
||||
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
|
||||
|
||||
if ($transparent !== false) {
|
||||
//simpler than flood fill
|
||||
if (imagefilledrectangle($dst, 0, 0, imagesx($image), imagesy($image), $transparent) !== false) {
|
||||
//restore default blending
|
||||
if (imagealphablending($dst, true) !== false) {
|
||||
if (imagecopy($dst, $image, 0, 0, 0, 0, imagesx($image), imagesy($image)) !== false) {
|
||||
$success = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success) {
|
||||
imagedestroy($image);
|
||||
$image = $dst;
|
||||
} else {
|
||||
imagedestroy($dst);
|
||||
}
|
||||
return $success;
|
||||
} else {
|
||||
// The necessary methods for converting color palette are not avalaible
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to convert image pallette to true color.
|
||||
*
|
||||
* Try to convert image pallette to true color. If imagepalettetotruecolor() exists, that is used (available from
|
||||
* PHP >= 5.5.0). Otherwise using workaround found on the net.
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return boolean TRUE if the convertion was complete, or if the source image already is a true color image,
|
||||
* otherwise FALSE is returned.
|
||||
*/
|
||||
private function makeTrueColor(&$image)
|
||||
{
|
||||
if (function_exists('imagepalettetotruecolor')) {
|
||||
return imagepalettetotruecolor($image);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'imagepalettetotruecolor() is not available on this system - using custom implementation instead'
|
||||
);
|
||||
return $this->makeTrueColorUsingWorkaround($image);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Gd image resource from source
|
||||
*
|
||||
* @throws InvalidInputException if mime type is unsupported or could not be detected
|
||||
* @throws ConversionFailedException if imagecreatefrompng or imagecreatefromjpeg fails
|
||||
* @return resource|\GdImage $image The created image
|
||||
*/
|
||||
private function createImageResource()
|
||||
{
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
$image = imagecreatefrompng($this->source);
|
||||
if ($image === false) {
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to load/create image (imagecreatefrompng() failed)'
|
||||
);
|
||||
}
|
||||
return $image;
|
||||
|
||||
case 'image/jpeg':
|
||||
$image = imagecreatefromjpeg($this->source);
|
||||
if ($image === false) {
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to load/create image (imagecreatefromjpeg() failed)'
|
||||
);
|
||||
}
|
||||
return $image;
|
||||
}
|
||||
|
||||
throw new InvalidInputException('Unsupported mime type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to make image resource true color if it is not already.
|
||||
*
|
||||
* @param resource|\GdImage $image The image to work on
|
||||
* @return boolean|null True if it is now true color. False if it is NOT true color. null, if we cannot tell
|
||||
*/
|
||||
protected function tryToMakeTrueColorIfNot(&$image)
|
||||
{
|
||||
$whatIsItNow = null;
|
||||
$mustMakeTrueColor = false;
|
||||
if (function_exists('imageistruecolor')) {
|
||||
if (imageistruecolor($image)) {
|
||||
$this->logLn('image is true color');
|
||||
$whatIsItNow = true;
|
||||
} else {
|
||||
$this->logLn('image is not true color');
|
||||
$mustMakeTrueColor = true;
|
||||
$whatIsItNow = false;
|
||||
}
|
||||
} else {
|
||||
$this->logLn('It can not be determined if image is true color');
|
||||
$mustMakeTrueColor = true;
|
||||
}
|
||||
|
||||
if ($mustMakeTrueColor) {
|
||||
$this->logLn('converting color palette to true color');
|
||||
$success = $this->makeTrueColor($image);
|
||||
if ($success) {
|
||||
return true;
|
||||
} else {
|
||||
$this->logLn(
|
||||
'FAILED converting color palette to true color. '
|
||||
);
|
||||
}
|
||||
}
|
||||
return $whatIsItNow;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return boolean true if alpha blending was set successfully, false otherwise
|
||||
*/
|
||||
protected function trySettingAlphaBlending($image)
|
||||
{
|
||||
if (function_exists('imagealphablending')) {
|
||||
// TODO: Should we set second parameter to false instead?
|
||||
// As here: https://www.texelate.co.uk/blog/retaining-png-transparency-with-php-gd
|
||||
// (PS: I have backed up some local changes - to Gd.php, which includes changing that param
|
||||
// to false. But I didn't finish up back then and now I forgot, so need to retest before
|
||||
// changing anything...
|
||||
if (!imagealphablending($image, true)) {
|
||||
$this->logLn('Warning: imagealphablending() failed');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Warning: imagealphablending() is not available on your system.' .
|
||||
' Converting PNGs with transparency might fail on some systems'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (function_exists('imagesavealpha')) {
|
||||
if (!imagesavealpha($image, true)) {
|
||||
$this->logLn('Warning: imagesavealpha() failed');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Warning: imagesavealpha() is not available on your system. ' .
|
||||
'Converting PNGs with transparency might fail on some systems'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function errorHandlerWhileCreatingWebP($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
$this->errorNumberWhileCreating = $errno;
|
||||
$this->errorMessageWhileCreating = $errstr . ' in ' . $errfile . ', line ' . $errline .
|
||||
', PHP ' . PHP_VERSION . ' (' . PHP_OS . ')';
|
||||
//return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return void
|
||||
*/
|
||||
protected function destroyAndRemove($image)
|
||||
{
|
||||
imagedestroy($image);
|
||||
if (file_exists($this->destination)) {
|
||||
unlink($this->destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param resource|\GdImage $image
|
||||
* @return void
|
||||
*/
|
||||
protected function tryConverting($image)
|
||||
{
|
||||
|
||||
// Danger zone!
|
||||
// Using output buffering to generate image.
|
||||
// In this zone, Do NOT do anything that might produce unwanted output
|
||||
// Do NOT call $this->logLn
|
||||
// --------------------------------- (start of danger zone)
|
||||
|
||||
$addedZeroPadding = false;
|
||||
set_error_handler(array($this, "errorHandlerWhileCreatingWebP"));
|
||||
|
||||
// This line may trigger log, so we need to do it BEFORE ob_start() !
|
||||
$q = $this->getCalculatedQuality();
|
||||
|
||||
ob_start();
|
||||
|
||||
// Adding this try/catch is perhaps not neccessary.
|
||||
// I'm not certain that the error handler takes care of Throwable errors.
|
||||
// and - sorry - was to lazy to find out right now. So for now: better safe than sorry. #320
|
||||
$error = null;
|
||||
$success = false;
|
||||
|
||||
try {
|
||||
// Beware: This call can throw FATAL on windows (cannot be catched)
|
||||
// This for example happens on palette images
|
||||
$success = imagewebp($image, null, $q);
|
||||
} catch (\Exception $e) {
|
||||
$error = $e;
|
||||
} catch (\Throwable $e) {
|
||||
$error = $e;
|
||||
}
|
||||
if (!is_null($error)) {
|
||||
restore_error_handler();
|
||||
ob_end_clean();
|
||||
$this->destroyAndRemove($image);
|
||||
throw $error;
|
||||
}
|
||||
if (!$success) {
|
||||
$this->destroyAndRemove($image);
|
||||
ob_end_clean();
|
||||
restore_error_handler();
|
||||
throw new ConversionFailedException(
|
||||
'Failed creating image. Call to imagewebp() failed.',
|
||||
$this->errorMessageWhileCreating
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// The following hack solves an `imagewebp` bug
|
||||
// See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
|
||||
if (ob_get_length() % 2 == 1) {
|
||||
echo "\0";
|
||||
$addedZeroPadding = true;
|
||||
}
|
||||
$output = ob_get_clean();
|
||||
restore_error_handler();
|
||||
|
||||
if ($output == '') {
|
||||
$this->destroyAndRemove($image);
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed: imagewebp() returned empty string'
|
||||
);
|
||||
}
|
||||
|
||||
// --------------------------------- (end of danger zone).
|
||||
|
||||
|
||||
if ($this->errorMessageWhileCreating != '') {
|
||||
switch ($this->errorNumberWhileCreating) {
|
||||
case E_WARNING:
|
||||
$this->logLn('An warning was produced during conversion: ' . $this->errorMessageWhileCreating);
|
||||
break;
|
||||
case E_NOTICE:
|
||||
$this->logLn('An notice was produced during conversion: ' . $this->errorMessageWhileCreating);
|
||||
break;
|
||||
default:
|
||||
$this->destroyAndRemove($image);
|
||||
throw new ConversionFailedException(
|
||||
'An error was produced during conversion',
|
||||
$this->errorMessageWhileCreating
|
||||
);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($addedZeroPadding) {
|
||||
$this->logLn(
|
||||
'Fixing corrupt webp by adding a zero byte ' .
|
||||
'(older versions of Gd had a bug, but this hack fixes it)'
|
||||
);
|
||||
}
|
||||
|
||||
$success = file_put_contents($this->destination, $output);
|
||||
|
||||
if (!$success) {
|
||||
$this->destroyAndRemove($image);
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to save the image. Check file permissions!'
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Previous code was much simpler, but on a system, the hack was not activated (a file with uneven number of bytes
|
||||
was created). This is puzzeling. And the old code did not provide any insights.
|
||||
Also, perhaps having two subsequent writes to the same file could perhaps cause a problem.
|
||||
In the new code, there is only one write.
|
||||
However, a bad thing about the new code is that the entire webp file is read into memory. This might cause
|
||||
memory overflow with big files.
|
||||
Perhaps we should check the filesize of the original and only use the new code when it is smaller than
|
||||
memory limit set in PHP by a certain factor.
|
||||
Or perhaps only use the new code on older versions of Gd
|
||||
https://wordpress.org/support/topic/images-not-seen-on-chrome/#post-11390284
|
||||
|
||||
Here is the old code:
|
||||
|
||||
$success = imagewebp($image, $this->destination, $this->getCalculatedQuality());
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException(
|
||||
'Gd failed when trying to save the image as webp (call to imagewebp() failed). ' .
|
||||
'It probably failed writing file. Check file permissions!'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// This hack solves an `imagewebp` bug
|
||||
// See https://stackoverflow.com/questions/30078090/imagewebp-php-creates-corrupted-webp-files
|
||||
if (filesize($this->destination) % 2 == 1) {
|
||||
file_put_contents($this->destination, "\0", FILE_APPEND);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Although this method is public, do not call directly.
|
||||
// You should rather call the static convert() function, defined in AbstractConverter, which
|
||||
// takes care of preparing stuff before calling doConvert, and validating after.
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$versionString = gd_info()["GD Version"];
|
||||
$this->logLn('GD Version: ' . $versionString);
|
||||
|
||||
// Btw: Check out processWebp here:
|
||||
// https://github.com/Intervention/image/blob/master/src/Intervention/Image/Gd/Encoder.php
|
||||
|
||||
// Create image resource
|
||||
$image = $this->createImageResource();
|
||||
|
||||
// Try to convert color palette if it is not true color
|
||||
$isItTrueColorNow = $this->tryToMakeTrueColorIfNot($image);
|
||||
if ($isItTrueColorNow === false) {
|
||||
// our tests shows that converting palette fails on all systems,
|
||||
throw new ConversionFailedException(
|
||||
'Cannot convert image because it is a palette image and the palette image cannot ' .
|
||||
'be converted to RGB (which is required). To convert to RGB, we would need ' .
|
||||
'imagepalettetotruecolor(), which is not available on your system. ' .
|
||||
'Our workaround does not have the neccasary functions for converting to RGB either.'
|
||||
);
|
||||
}
|
||||
if (is_null($isItTrueColorNow)) {
|
||||
$isWindows = preg_match('/^win/i', PHP_OS);
|
||||
$isMacDarwin = preg_match('/^darwin/i', PHP_OS); // actually no problem in PHP 7.4 and 8.0
|
||||
if ($isWindows || $isMacDarwin) {
|
||||
throw new ConversionFailedException(
|
||||
'Cannot convert image because it appears to be a palette image and the palette image ' .
|
||||
'cannot be converted to RGB, as you do not have imagepalettetotruecolor() enabled. ' .
|
||||
'Converting palette on ' .
|
||||
($isWindows ? 'Windows causes FATAL error' : 'Mac causes halt') .
|
||||
'So we abort now'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getMimeTypeOfSource() == 'image/png') {
|
||||
if (function_exists('version_compare')) {
|
||||
if (version_compare($versionString, "2.1.1", "<=")) {
|
||||
$this->logLn(
|
||||
'BEWARE: Your version of Gd looses the alpha chanel when converting to webp.' .
|
||||
'You should upgrade Gd, use another converter or stop converting PNGs. ' .
|
||||
'See: https://github.com/rosell-dk/webp-convert/issues/238'
|
||||
);
|
||||
} elseif (version_compare($versionString, "2.2.4", "<=")) {
|
||||
$this->logLn(
|
||||
'BEWARE: Older versions of Gd looses the alpha chanel when converting to webp.' .
|
||||
'We have not tested if transparency fails on your (rather old) version of Gd. ' .
|
||||
'Please let us know. ' .
|
||||
'See: https://github.com/rosell-dk/webp-convert/issues/238'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to set alpha blending
|
||||
$this->trySettingAlphaBlending($image);
|
||||
}
|
||||
|
||||
// Try to convert it to webp
|
||||
$this->tryConverting($image);
|
||||
|
||||
// End of story
|
||||
imagedestroy($image);
|
||||
}
|
||||
}
|
||||
173
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Gmagick.php
vendored
Normal file
173
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Gmagick.php
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp using Gmagick extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Gmagick extends AbstractConverter
|
||||
{
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'near-lossless',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of Gmagick converter.
|
||||
*
|
||||
* Note:
|
||||
* It may be that Gd has been compiled without jpeg support or png support.
|
||||
* We do not check for this here, as the converter could still be used for the other.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('Gmagick')) {
|
||||
throw new SystemRequirementsNotMetException('Required Gmagick extension is not available.');
|
||||
}
|
||||
|
||||
if (!class_exists('Gmagick')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gmagick is installed, but not correctly. The class Gmagick is not available'
|
||||
);
|
||||
}
|
||||
|
||||
$im = new \Gmagick($this->source);
|
||||
|
||||
if (!in_array('WEBP', $im->queryformats())) {
|
||||
throw new SystemRequirementsNotMetException('Gmagick was compiled without WebP support.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Gmagick does not support image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$im = new \Gmagick();
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
if (!in_array('PNG', $im->queryFormats())) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gmagick has been compiled without PNG support and can therefore not convert this PNG image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'image/jpeg':
|
||||
if (!in_array('JPEG', $im->queryFormats())) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Gmagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Although this method is public, do not call directly.
|
||||
// You should rather call the static convert() function, defined in AbstractConverter, which
|
||||
// takes care of preparing stuff before calling doConvert, and validating after.
|
||||
protected function doActualConvert()
|
||||
{
|
||||
|
||||
// PS: graphicsmagick options are documented here: (search for "webp:")
|
||||
// http://www.graphicsmagick.org/GraphicsMagick.html
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
try {
|
||||
$im = new \Gmagick($this->source);
|
||||
} catch (\Exception $e) {
|
||||
throw new ConversionFailedException(
|
||||
'Failed creating Gmagick object of file',
|
||||
'Failed creating Gmagick object of file: "' . $this->source . '" - Gmagick threw an exception.',
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
$im->setimageformat('WEBP');
|
||||
|
||||
// setimageoption() has not always been there, so check first. #169
|
||||
if (method_exists($im, 'setimageoption')) {
|
||||
// Finally cracked setting webp options.
|
||||
// See #167
|
||||
// - and https://stackoverflow.com/questions/47294962/how-to-write-lossless-webp-files-with-perlmagick
|
||||
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'The "preset" value was mapped to "graph" because gmagick does not support "drawing",' .
|
||||
' "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$im->setimageoption('webp', 'image-hint', $imageHint);
|
||||
}
|
||||
}
|
||||
$im->setimageoption('webp', 'method', $options['method']);
|
||||
$im->setimageoption('webp', 'lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
|
||||
$im->setimageoption('webp', 'alpha-quality', $options['alpha-quality']);
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
$im->setimageoption('webp', 'auto-filter', 'true');
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
$im->setimageoption('webp', 'use-sharp-yuv', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
low-memory seems not to be supported:
|
||||
$im->setimageoption('webp', 'low-memory', $options['low-memory'] ? true : false);
|
||||
*/
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
// Strip metadata and profiles
|
||||
$im->stripImage();
|
||||
}
|
||||
|
||||
// Ps: Imagick automatically uses same quality as source, when no quality is set
|
||||
// This feature is however not present in Gmagick
|
||||
// TODO: However, it might be possible after all - see #91
|
||||
$im->setcompressionquality($this->getCalculatedQuality());
|
||||
|
||||
// We call getImageBlob().
|
||||
// That method is undocumented, but it is there!
|
||||
// - just like it is in imagick, as pointed out here:
|
||||
// https://www.php.net/manual/ru/gmagick.readimageblob.php
|
||||
|
||||
/** @scrutinizer ignore-call */
|
||||
$imageBlob = $im->getImageBlob();
|
||||
|
||||
$success = @file_put_contents($this->destination, $imageBlob);
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException('Failed writing file');
|
||||
}
|
||||
}
|
||||
}
|
||||
28
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/GmagickBinary.php
vendored
Normal file
28
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/GmagickBinary.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
/**
|
||||
* Non-functional converter, just here to tell you that it has been renamed.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class GmagickBinary extends AbstractConverter
|
||||
{
|
||||
public function checkOperationality()
|
||||
{
|
||||
throw new ConversionFailedException(
|
||||
'This converter has changed ID from "gmagickbinary" to "graphicsmagick". You need to change!'
|
||||
);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$this->checkOperationality();
|
||||
}
|
||||
}
|
||||
220
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/GraphicsMagick.php
vendored
Normal file
220
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/GraphicsMagick.php
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling gmagick binary (gm).
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class GraphicsMagick extends AbstractConverter
|
||||
{
|
||||
use ExecTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'near-lossless',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption()
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPath()
|
||||
{
|
||||
if (defined('WEBPCONVERT_GRAPHICSMAGICK_PATH')) {
|
||||
return constant('WEBPCONVERT_GRAPHICSMAGICK_PATH');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH'))) {
|
||||
return getenv('WEBPCONVERT_GRAPHICSMAGICK_PATH');
|
||||
}
|
||||
return 'gm';
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
return ($returnCode == 0);
|
||||
}
|
||||
|
||||
public function getVersion()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
if (($returnCode == 0) && isset($output[0])) {
|
||||
return preg_replace('#http.*#', '', $output[0]);
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// Check if webp delegate is installed
|
||||
public function isWebPDelegateInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('#WebP.*yes#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
if (!$this->isInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('gmagick is not installed');
|
||||
}
|
||||
if (!$this->isWebPDelegateInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('webp delegate missing');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions()
|
||||
{
|
||||
// For available webp options, check out:
|
||||
// https://github.com/kstep/graphicsmagick/blob/master/coders/webp.c
|
||||
|
||||
$commandArguments = [];
|
||||
|
||||
/*
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// Unlike imagick binary, it seems gmagick binary uses a fixed
|
||||
// quality (75) when quality is omitted
|
||||
// So we cannot simply omit in order to get same quality as source.
|
||||
// But perhaps there is another way?
|
||||
// Check out #91 - it is perhaps as easy as this: "-define jpeg:preserve-settings"
|
||||
}
|
||||
*/
|
||||
$commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
// preset
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'Note: the preset was mapped to "graph" because graphicsmagick does not support ' .
|
||||
'"drawing", "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$commandArguments[] = '-define webp:image-hint=' . escapeshellarg($imageHint);
|
||||
}
|
||||
}
|
||||
|
||||
// encoding
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
// Btw:
|
||||
// I am not sure if we should set "quality" for lossless.
|
||||
// Quality should not apply to lossless, but my tests shows that it does in some way for gmagick
|
||||
// setting it low, you get bad visual quality and small filesize. Setting it high, you get the opposite
|
||||
// Some claim it is a bad idea to set quality, but I'm not so sure.
|
||||
// https://stackoverflow.com/questions/4228027/
|
||||
// First, I do not just get bigger images when setting quality, as toc777 does.
|
||||
// Secondly, the answer is very old and that bad behaviour is probably fixed by now.
|
||||
$commandArguments[] = '-define webp:lossless=true';
|
||||
} else {
|
||||
$commandArguments[] = '-define webp:lossless=false';
|
||||
}
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
$commandArguments[] = '-define webp:auto-filter=true';
|
||||
}
|
||||
|
||||
if ($options['alpha-quality'] !== 100) {
|
||||
$commandArguments[] = '-define webp:alpha-quality=' . strval($options['alpha-quality']);
|
||||
}
|
||||
|
||||
if ($options['low-memory']) {
|
||||
$commandArguments[] = '-define webp:low-memory=true';
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
$commandArguments[] = '-define webp:use-sharp-yuv=true';
|
||||
}
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
$commandArguments[] = '-strip';
|
||||
}
|
||||
|
||||
$commandArguments[] = '-define webp:method=' . $options['method'];
|
||||
|
||||
$commandArguments[] = escapeshellarg($this->source);
|
||||
$commandArguments[] = escapeshellarg('webp:' . $this->destination);
|
||||
|
||||
return implode(' ', $commandArguments);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
//$this->logLn('Using quality:' . $this->getCalculatedQuality());
|
||||
|
||||
$this->logLn('Version: ' . $this->getVersion());
|
||||
|
||||
$command = $this->getPath() . ' convert ' . $this->createCommandLineOptions() . ' 2>&1';
|
||||
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
if ($useNice) {
|
||||
$command = 'nice ' . $command;
|
||||
}
|
||||
$this->logLn('Executing command: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
$this->logExecOutput($output);
|
||||
if ($returnCode == 0) {
|
||||
$this->logLn('success');
|
||||
} else {
|
||||
$this->logLn('return code: ' . $returnCode);
|
||||
}
|
||||
|
||||
if ($returnCode == 127) {
|
||||
throw new SystemRequirementsNotMetException('gmagick is not installed');
|
||||
}
|
||||
if ($returnCode != 0) {
|
||||
$this->logLn('command:' . $command);
|
||||
$this->logLn('return code:' . $returnCode);
|
||||
$this->logLn('output:' . print_r(implode("\n", $output), true));
|
||||
throw new SystemRequirementsNotMetException('The exec() call failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
275
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/ImageMagick.php
vendored
Normal file
275
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/ImageMagick.php
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
use LocateBinaries\LocateBinaries;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by calling imagemagick binary.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ImageMagick extends AbstractConverter
|
||||
{
|
||||
use ExecTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
self::niceOption(),
|
||||
['try-common-system-paths', 'boolean', [
|
||||
'title' => 'Try locating ImageMagick in common system paths',
|
||||
'description' =>
|
||||
'If set, the converter will look for a ImageMagick binaries residing in common system locations ' .
|
||||
'such as "/usr/bin/convert". ' .
|
||||
'If such exist, it is assumed that they are valid ImageMagick binaries. ',
|
||||
'default' => true,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
]);
|
||||
}
|
||||
|
||||
// To futher improve this converter, I could check out:
|
||||
// https://github.com/Orbitale/ImageMagickPHP
|
||||
|
||||
private function getPath()
|
||||
{
|
||||
if (defined('WEBPCONVERT_IMAGEMAGICK_PATH')) {
|
||||
return constant('WEBPCONVERT_IMAGEMAGICK_PATH');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_IMAGEMAGICK_PATH'))) {
|
||||
return getenv('WEBPCONVERT_IMAGEMAGICK_PATH');
|
||||
}
|
||||
|
||||
if ($this->options['try-common-system-paths']) {
|
||||
$binaries = LocateBinaries::locateInCommonSystemPaths('convert');
|
||||
if (!empty($binaries)) {
|
||||
return $binaries[0];
|
||||
}
|
||||
}
|
||||
|
||||
return 'convert';
|
||||
}
|
||||
|
||||
private function getVersion()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
if (($returnCode == 0) && isset($output[0])) {
|
||||
return $output[0];
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
|
||||
return ($returnCode == 0);
|
||||
}
|
||||
|
||||
// Check if webp delegate is installed
|
||||
public function isWebPDelegateInstalled()
|
||||
{
|
||||
ExecWithFallback::exec($this->getPath() . ' -list delegate 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('#webp\\s*=#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// try other command
|
||||
ExecWithFallback::exec($this->getPath() . ' -list configure 2>&1', $output, $returnCode);
|
||||
foreach ($output as $line) {
|
||||
if (preg_match('#DELEGATE.*webp#i', $line)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
// PS, convert -version does not output delegates on travis, so it is not reliable
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
$this->checkOperationalityExecTrait();
|
||||
|
||||
if (!$this->isInstalled()) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'imagemagick is not installed (cannot execute: "' . $this->getPath() . '")'
|
||||
);
|
||||
}
|
||||
if (!$this->isWebPDelegateInstalled()) {
|
||||
throw new SystemRequirementsNotMetException('webp delegate missing');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build command line options
|
||||
*
|
||||
* @param string $versionNumber. Ie "6.9.10-23"
|
||||
* @return string
|
||||
*/
|
||||
private function createCommandLineOptions($versionNumber = 'unknown')
|
||||
{
|
||||
// Available webp options for imagemagick are documented here:
|
||||
// - https://imagemagick.org/script/webp.php
|
||||
// - https://github.com/ImageMagick/ImageMagick/blob/main/coders/webp.c
|
||||
|
||||
// We should perhaps implement low-memory. Its already in cwebp, it
|
||||
// could perhaps be promoted to a general option
|
||||
|
||||
$commandArguments = [];
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// quality:auto was specified, but could not be determined.
|
||||
// we cannot apply the max-quality logic, but we can provide auto quality
|
||||
// simply by not specifying the quality option.
|
||||
} else {
|
||||
$commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
|
||||
}
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
if (!is_null($options['preset'])) {
|
||||
// "image-hint" is at least available from 6.9.4-0 (I can't see further back)
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'The "preset" value was mapped to "graph" because imagemagick does not support "drawing",' .
|
||||
' "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$commandArguments[] = '-define webp:image-hint=' . escapeshellarg($imageHint);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['encoding'] == 'lossless') {
|
||||
// lossless is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:lossless=true';
|
||||
}
|
||||
|
||||
if ($options['low-memory']) {
|
||||
// low-memory is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:low-memory=true';
|
||||
}
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
// auto-filter is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:auto-filter=true';
|
||||
}
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
$commandArguments[] = '-strip';
|
||||
}
|
||||
|
||||
if ($options['alpha-quality'] !== 100) {
|
||||
// alpha-quality is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:alpha-quality=' . strval($options['alpha-quality']);
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
if (version_compare($versionNumber, '7.0.8-26', '>=')) {
|
||||
$commandArguments[] = '-define webp:use-sharp-yuv=true';
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: "sharp-yuv" option is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.8-26 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['near-lossless'] != 100) {
|
||||
if (version_compare($versionNumber, '7.0.10-54', '>=')) { // #299
|
||||
$commandArguments[] = '-define webp:near-lossless=' . escapeshellarg($options['near-lossless']);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: "near-lossless" option is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.10-54 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// "method" is at least available from 6.9.4-0 (I can't see further back)
|
||||
$commandArguments[] = '-define webp:method=' . $options['method'];
|
||||
|
||||
$commandArguments[] = escapeshellarg($this->source);
|
||||
$commandArguments[] = escapeshellarg('webp:' . $this->destination);
|
||||
|
||||
return implode(' ', $commandArguments);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$version = $this->getVersion();
|
||||
|
||||
$this->logLn($version);
|
||||
|
||||
preg_match('#\d+\.\d+\.\d+[\d\.\-]+#', $version, $matches);
|
||||
$versionNumber = (isset($matches[0]) ? $matches[0] : 'unknown');
|
||||
|
||||
$this->logLn('Extracted version number: ' . $versionNumber);
|
||||
|
||||
$command = $this->getPath() . ' ' . $this->createCommandLineOptions($versionNumber) . ' 2>&1';
|
||||
|
||||
$useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
|
||||
if ($useNice) {
|
||||
$command = 'nice ' . $command;
|
||||
}
|
||||
$this->logLn('Executing command: ' . $command);
|
||||
ExecWithFallback::exec($command, $output, $returnCode);
|
||||
|
||||
$this->logExecOutput($output);
|
||||
if ($returnCode == 0) {
|
||||
$this->logLn('success');
|
||||
} else {
|
||||
$this->logLn('return code: ' . $returnCode);
|
||||
}
|
||||
|
||||
if ($returnCode == 127) {
|
||||
throw new SystemRequirementsNotMetException('imagemagick is not installed');
|
||||
}
|
||||
if ($returnCode != 0) {
|
||||
throw new SystemRequirementsNotMetException('The exec call failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
229
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Imagick.php
vendored
Normal file
229
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Imagick.php
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems\CreateDestinationFileException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp using Imagick extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Imagick extends AbstractConverter
|
||||
{
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check operationality of Imagick converter.
|
||||
*
|
||||
* Note:
|
||||
* It may be that Gd has been compiled without jpeg support or png support.
|
||||
* We do not check for this here, as the converter could still be used for the other.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
* @return void
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('imagick')) {
|
||||
throw new SystemRequirementsNotMetException('Required iMagick extension is not available.');
|
||||
}
|
||||
|
||||
if (!class_exists('\\Imagick')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'iMagick is installed, but not correctly. The class Imagick is not available'
|
||||
);
|
||||
}
|
||||
|
||||
$im = new \Imagick();
|
||||
if (!in_array('WEBP', $im->queryFormats('WEBP'))) {
|
||||
throw new SystemRequirementsNotMetException('iMagick was compiled without WebP support.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Imagick does not support image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
$im = new \Imagick();
|
||||
$mimeType = $this->getMimeTypeOfSource();
|
||||
switch ($mimeType) {
|
||||
case 'image/png':
|
||||
if (!in_array('PNG', $im->queryFormats('PNG'))) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Imagick has been compiled without PNG support and can therefore not convert this PNG image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'image/jpeg':
|
||||
if (!in_array('JPEG', $im->queryFormats('JPEG'))) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Imagick has been compiled without Jpeg support and can therefore not convert this Jpeg image.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* It may also throw an ImagickException if imagick throws an exception
|
||||
* @throws CreateDestinationFileException if imageblob could not be saved to file
|
||||
*/
|
||||
protected function doActualConvert()
|
||||
{
|
||||
/*
|
||||
* More about iMagick's WebP options:
|
||||
* - Inspect source code: https://github.com/ImageMagick/ImageMagick/blob/master/coders/webp.c#L559
|
||||
* (search for "webp:")
|
||||
* - http://www.imagemagick.org/script/webp.php
|
||||
* - https://developers.google.com/speed/webp/docs/cwebp
|
||||
* - https://stackoverflow.com/questions/37711492/imagemagick-specific-webp-calls-in-php
|
||||
*/
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
// This might throw - we let it!
|
||||
$im = new \Imagick($this->source);
|
||||
//$im = new \Imagick();
|
||||
//$im->pingImage($this->source);
|
||||
//$im->readImage($this->source);
|
||||
|
||||
$version = \Imagick::getVersion();
|
||||
$this->logLn('ImageMagic API version (full): ' . $version['versionString']);
|
||||
|
||||
preg_match('#\d+\.\d+\.\d+[\d\.\-]+#', $version['versionString'], $matches);
|
||||
$versionNumber = (isset($matches[0]) ? $matches[0] : 'unknown');
|
||||
$this->logLn('ImageMagic API version (just the number): ' . $versionNumber);
|
||||
|
||||
// Note: good enough for info, but not entirely reliable - see #304
|
||||
$extVersion = (defined('\Imagick::IMAGICK_EXTVER') ? \Imagick::IMAGICK_EXTVER : phpversion('imagick'));
|
||||
$this->logLn('Imagic extension version: ' . $extVersion);
|
||||
|
||||
$im->setImageFormat('WEBP');
|
||||
|
||||
if (!is_null($options['preset'])) {
|
||||
if ($options['preset'] != 'none') {
|
||||
$imageHint = $options['preset'];
|
||||
switch ($imageHint) {
|
||||
case 'drawing':
|
||||
case 'icon':
|
||||
case 'text':
|
||||
$imageHint = 'graph';
|
||||
$this->logLn(
|
||||
'The "preset" value was mapped to "graph" because imagick does not support "drawing",' .
|
||||
' "icon" and "text", but grouped these into one option: "graph".'
|
||||
);
|
||||
}
|
||||
$im->setOption('webp:image-hint', $imageHint);
|
||||
}
|
||||
}
|
||||
|
||||
$im->setOption('webp:method', $options['method']);
|
||||
$im->setOption('webp:lossless', $options['encoding'] == 'lossless' ? 'true' : 'false');
|
||||
$im->setOption('webp:low-memory', $options['low-memory'] ? 'true' : 'false');
|
||||
$im->setOption('webp:alpha-quality', $options['alpha-quality']);
|
||||
|
||||
if ($options['near-lossless'] != 100) {
|
||||
if (version_compare($versionNumber, '7.0.10-54', '>=')) {
|
||||
$im->setOption('webp:near-lossless', $options['near-lossless']);
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: near-lossless is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.10-54 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['auto-filter'] === true) {
|
||||
$im->setOption('webp:auto-filter', 'true');
|
||||
}
|
||||
|
||||
if ($options['sharp-yuv'] === true) {
|
||||
if (version_compare($versionNumber, '7.0.8-26', '>=')) {
|
||||
$im->setOption('webp:use-sharp-yuv', 'true');
|
||||
} else {
|
||||
$this->logLn(
|
||||
'Note: "sharp-yuv" option is not supported in your version of ImageMagick. ' .
|
||||
'ImageMagic >= 7.0.8-26 is required',
|
||||
'italic'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['metadata'] == 'none') {
|
||||
// To strip metadata, we need to use the stripImage() method. However, that method does not only remove
|
||||
// metadata, but color profiles as well. We want to keep the color profiles, so we grab it now to be able
|
||||
// to restore it. (Thanks, Max Eremin: https://www.php.net/manual/en/imagick.stripimage.php#120380)
|
||||
|
||||
// Grab color profile (to be able to restore them)
|
||||
$profiles = $im->getImageProfiles("icc", true);
|
||||
|
||||
// Strip metadata (and color profiles)
|
||||
$im->stripImage();
|
||||
|
||||
// Restore color profiles
|
||||
if (!empty($profiles)) {
|
||||
$im->profileImage("icc", $profiles['icc']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// Luckily imagick is a big boy, and automatically converts with same quality as
|
||||
// source, when the quality isn't set.
|
||||
// So we simply do not set quality.
|
||||
// This actually kills the max-quality functionality. But I deem that this is more important
|
||||
// because setting image quality to something higher than source generates bigger files,
|
||||
// but gets you no extra quality. When failing to limit quality, you at least get something
|
||||
// out of it
|
||||
$this->logLn('Converting without setting quality in order to achieve auto quality');
|
||||
} else {
|
||||
$im->setImageCompressionQuality($this->getCalculatedQuality());
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/29171248/php-imagick-jpeg-optimization
|
||||
// setImageFormat
|
||||
|
||||
// TODO: Read up on
|
||||
// https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
|
||||
// https://github.com/nwtn/php-respimg
|
||||
|
||||
// TODO:
|
||||
// Should we set alpha channel for PNG's like suggested here:
|
||||
// https://gauntface.com/blog/2014/09/02/webp-support-with-imagemagick-and-php ??
|
||||
// It seems that alpha channel works without... (at least I see completely transparerent pixels)
|
||||
|
||||
// We used to use writeImageFile() method. But we now use getImageBlob(). See issue #43
|
||||
|
||||
// This might throw - we let it!
|
||||
$imageBlob = $im->getImageBlob();
|
||||
|
||||
$success = file_put_contents($this->destination, $imageBlob);
|
||||
|
||||
if (!$success) {
|
||||
throw new CreateDestinationFileException('Failed writing file');
|
||||
}
|
||||
|
||||
// Btw: check out processWebp() method here:
|
||||
// https://github.com/Intervention/image/blob/master/src/Intervention/Image/Imagick/Encoder.php
|
||||
}
|
||||
}
|
||||
28
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/ImagickBinary.php
vendored
Normal file
28
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/ImagickBinary.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
/**
|
||||
* Non-functional converter, just here to tell you that it has been renamed.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ImagickBinary extends AbstractConverter
|
||||
{
|
||||
public function checkOperationality()
|
||||
{
|
||||
throw new ConversionFailedException(
|
||||
'This converter has changed ID from "imagickbinary" to "imagemagick". You need to change!'
|
||||
);
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$this->checkOperationality();
|
||||
}
|
||||
}
|
||||
283
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Stack.php
vendored
Normal file
283
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Stack.php
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\ConverterFactory;
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConversionSkippedException;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
//use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Convert images to webp by trying a stack of converters until success.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Stack extends AbstractConverter
|
||||
{
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'alpha-quality',
|
||||
'auto-filter',
|
||||
'encoding',
|
||||
'low-memory',
|
||||
'metadata',
|
||||
'method',
|
||||
'near-lossless',
|
||||
'preset',
|
||||
'sharp-yuv',
|
||||
'size-in-percentage',
|
||||
'skip',
|
||||
'default-quality',
|
||||
'quality',
|
||||
'max-quality',
|
||||
];
|
||||
}
|
||||
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
['converters', 'array', [
|
||||
'title' => 'Converters',
|
||||
'description' => 'Converters to try, ordered by priority.',
|
||||
'default' => self::getAvailableConverters(),
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'multi-select',
|
||||
'options' => self::getAvailableConverters(),
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
['converter-options', 'array', [
|
||||
'title' => 'Converter options',
|
||||
'description' =>
|
||||
'Extra options for specific converters.',
|
||||
'default' => [],
|
||||
'sensitive' => true,
|
||||
'ui' => null
|
||||
]],
|
||||
['preferred-converters', 'array', [
|
||||
'title' => 'Preferred converters',
|
||||
'description' =>
|
||||
'With this option you can move specified converters to the top of the stack. ' .
|
||||
'The converters are specified by id.',
|
||||
'default' => [],
|
||||
'ui' => null
|
||||
]],
|
||||
['extra-converters', 'array', [
|
||||
'title' => 'Extra converters',
|
||||
'description' =>
|
||||
'Add extra converters to the bottom of the stack',
|
||||
'default' => [],
|
||||
'sensitive' => true,
|
||||
'ui' => null
|
||||
]],
|
||||
['shuffle', 'boolean', [
|
||||
'title' => 'Shuffle',
|
||||
'description' =>
|
||||
'Shuffles the converter order on each conversion. ' .
|
||||
'Can for example be used to spread out requests on multiple cloud converters',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true
|
||||
]
|
||||
]],
|
||||
]);
|
||||
|
||||
|
||||
/*
|
||||
return [
|
||||
new SensitiveArrayOption('converters', self::getAvailableConverters()),
|
||||
new SensitiveArrayOption('converter-options', []),
|
||||
new BooleanOption('shuffle', false),
|
||||
new ArrayOption('preferred-converters', []),
|
||||
new SensitiveArrayOption('extra-converters', [])
|
||||
];*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available converters (ids) - ordered by awesomeness.
|
||||
*
|
||||
* @return array An array of ids of converters that comes with this library
|
||||
*/
|
||||
public static function getAvailableConverters()
|
||||
{
|
||||
return [
|
||||
'cwebp', 'vips', 'imagick', 'gmagick', 'imagemagick', 'graphicsmagick', 'wpc', 'ffmpeg', 'ewww', 'gd'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check (general) operationality of imagack converter executable
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (count($this->options['converters']) == 0) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Converter stack is empty! - no converters to try, no conversion can be made!'
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: We should test if all converters are found in order to detect problems early
|
||||
|
||||
//$this->logLn('Stack converter ignited');
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$options = $this->options;
|
||||
|
||||
$beginTimeStack = microtime(true);
|
||||
|
||||
$anyRuntimeErrors = false;
|
||||
|
||||
$converters = $options['converters'];
|
||||
if (count($options['extra-converters']) > 0) {
|
||||
$converters = array_merge($converters, $options['extra-converters']);
|
||||
/*foreach ($options['extra-converters'] as $extra) {
|
||||
$converters[] = $extra;
|
||||
}*/
|
||||
}
|
||||
|
||||
// preferred-converters
|
||||
if (count($options['preferred-converters']) > 0) {
|
||||
foreach (array_reverse($options['preferred-converters']) as $prioritizedConverter) {
|
||||
foreach ($converters as $i => $converter) {
|
||||
if (is_array($converter)) {
|
||||
$converterId = $converter['converter'];
|
||||
} else {
|
||||
$converterId = $converter;
|
||||
}
|
||||
if ($converterId == $prioritizedConverter) {
|
||||
unset($converters[$i]);
|
||||
array_unshift($converters, $converter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// perhaps write the order to the log? (without options) - but this requires some effort
|
||||
}
|
||||
|
||||
// shuffle
|
||||
if ($options['shuffle']) {
|
||||
shuffle($converters);
|
||||
}
|
||||
|
||||
//$this->logLn(print_r($converters));
|
||||
//$options['converters'] = $converters;
|
||||
//$defaultConverterOptions = $options;
|
||||
$defaultConverterOptions = [];
|
||||
|
||||
foreach ($this->options2->getOptionsMap() as $id => $option) {
|
||||
// Right here, there used to be a check that ensured that unknown options was not passed down to the
|
||||
// converters (" && !($option instanceof GhostOption)"). But well, as the Stack doesn't know about
|
||||
// converter specific options, such as "try-cwebp", these was not passed down (see #259)
|
||||
// I'm not sure why the check was made in the first place, but it does not seem neccessary, as the
|
||||
// converters simply ignore unknown options. So the check has now been removed.
|
||||
if ($option->isValueExplicitlySet()) {
|
||||
$defaultConverterOptions[$id] = $option->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
//unset($defaultConverterOptions['converters']);
|
||||
//unset($defaultConverterOptions['converter-options']);
|
||||
$defaultConverterOptions['_skip_input_check'] = true;
|
||||
$defaultConverterOptions['_suppress_success_message'] = true;
|
||||
unset($defaultConverterOptions['converters']);
|
||||
unset($defaultConverterOptions['extra-converters']);
|
||||
unset($defaultConverterOptions['converter-options']);
|
||||
unset($defaultConverterOptions['preferred-converters']);
|
||||
unset($defaultConverterOptions['shuffle']);
|
||||
|
||||
// $this->logLn('converters: ' . print_r($converters, true));
|
||||
|
||||
//return;
|
||||
foreach ($converters as $converter) {
|
||||
if (is_array($converter)) {
|
||||
$converterId = $converter['converter'];
|
||||
$converterOptions = isset($converter['options']) ? $converter['options'] : [];
|
||||
} else {
|
||||
$converterId = $converter;
|
||||
$converterOptions = [];
|
||||
if (isset($options['converter-options'][$converterId])) {
|
||||
// Note: right now, converter-options are not meant to be used,
|
||||
// when you have several converters of the same type
|
||||
$converterOptions = $options['converter-options'][$converterId];
|
||||
}
|
||||
}
|
||||
$converterOptions = array_merge($defaultConverterOptions, $converterOptions);
|
||||
/*
|
||||
if ($converterId != 'stack') {
|
||||
//unset($converterOptions['converters']);
|
||||
//unset($converterOptions['converter-options']);
|
||||
} else {
|
||||
//$converterOptions['converter-options'] =
|
||||
$this->logLn('STACK');
|
||||
$this->logLn('converterOptions: ' . print_r($converterOptions, true));
|
||||
}*/
|
||||
|
||||
$beginTime = microtime(true);
|
||||
|
||||
$this->ln();
|
||||
$this->logLn($converterId . ' converter ignited', 'bold');
|
||||
|
||||
$converter = ConverterFactory::makeConverter(
|
||||
$converterId,
|
||||
$this->source,
|
||||
$this->destination,
|
||||
$converterOptions,
|
||||
$this->logger
|
||||
);
|
||||
|
||||
try {
|
||||
$converter->doConvert();
|
||||
|
||||
//self::runConverterWithTiming($converterId, $source, $destination, $converterOptions, false, $logger);
|
||||
|
||||
$this->logLn($converterId . ' succeeded :)');
|
||||
//throw new ConverterNotOperationalException('...');
|
||||
return;
|
||||
} catch (ConverterNotOperationalException $e) {
|
||||
$this->logLn($e->getMessage());
|
||||
} catch (ConversionSkippedException $e) {
|
||||
$this->logLn($e->getMessage());
|
||||
} catch (ConversionFailedException $e) {
|
||||
$this->logLn($e->getMessage(), 'italic');
|
||||
$prev = $e->getPrevious();
|
||||
if (!is_null($prev)) {
|
||||
$this->logLn($prev->getMessage(), 'italic');
|
||||
$this->logLn(' in ' . $prev->getFile() . ', line ' . $prev->getLine(), 'italic');
|
||||
$this->ln();
|
||||
}
|
||||
//$this->logLn($e->getTraceAsString());
|
||||
$anyRuntimeErrors = true;
|
||||
}
|
||||
$this->logLn($converterId . ' failed in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
|
||||
}
|
||||
|
||||
$this->ln();
|
||||
$this->logLn('Stack failed in ' . round((microtime(true) - $beginTimeStack) * 1000) . ' ms');
|
||||
|
||||
// Hm, Scrutinizer complains that $anyRuntimeErrors is always false. But that is not true!
|
||||
if ($anyRuntimeErrors) {
|
||||
// At least one converter failed
|
||||
throw new ConversionFailedException(
|
||||
'None of the converters in the stack could convert the image.'
|
||||
);
|
||||
} else {
|
||||
// All converters threw a SystemRequirementsNotMetException
|
||||
throw new ConverterNotOperationalException('None of the converters in the stack are operational');
|
||||
}
|
||||
}
|
||||
}
|
||||
306
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Vips.php
vendored
Normal file
306
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Vips.php
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
|
||||
//require '/home/rosell/.composer/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* Convert images to webp using Vips extension.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Vips extends AbstractConverter
|
||||
{
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [
|
||||
'auto-filter',
|
||||
'size-in-percentage',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options unique for this converter
|
||||
*
|
||||
* @return array Array of options
|
||||
*/
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
$ssOption = new BooleanOption('smart-subsample', false);
|
||||
$ssOption->markDeprecated();
|
||||
return [
|
||||
$ssOption
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check operationality of Vips converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
if (!extension_loaded('vips')) {
|
||||
throw new SystemRequirementsNotMetException('Required Vips extension is not available.');
|
||||
}
|
||||
|
||||
if (!function_exists('vips_image_new_from_file')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips extension seems to be installed, however something is not right: ' .
|
||||
'the function "vips_image_new_from_file" is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists('vips_call')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips extension seems to be installed, however something is not right: ' .
|
||||
'the function "vips_call" is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists('vips_error_buffer')) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips extension seems to be installed, however something is not right: ' .
|
||||
'the function "vips_error_buffer" is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
vips_error_buffer(); // clear error buffer
|
||||
$result = vips_call('webpsave', null);
|
||||
if ($result === -1) {
|
||||
$message = vips_error_buffer();
|
||||
if (strpos($message, 'VipsOperation: class "webpsave" not found') === 0) {
|
||||
throw new SystemRequirementsNotMetException(
|
||||
'Vips has not been compiled with webp support.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific file is convertable with current converter / converter settings.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if Vips does not support image type
|
||||
*/
|
||||
public function checkConvertability()
|
||||
{
|
||||
// It seems that png and jpeg are always supported by Vips
|
||||
// - so nothing needs to be done here
|
||||
|
||||
if (function_exists('vips_version')) {
|
||||
$this->logLn('vipslib version: ' . vips_version());
|
||||
}
|
||||
$this->logLn('vips extension version: ' . phpversion('vips'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create vips image resource from source file
|
||||
*
|
||||
* @throws ConversionFailedException if image resource cannot be created
|
||||
* @return resource vips image resource
|
||||
*/
|
||||
private function createImageResource()
|
||||
{
|
||||
// We are currently using vips_image_new_from_file(), but we could consider
|
||||
// calling vips_jpegload / vips_pngload instead
|
||||
$result = /** @scrutinizer ignore-call */ vips_image_new_from_file($this->source, []);
|
||||
if ($result === -1) {
|
||||
/*throw new ConversionFailedException(
|
||||
'Failed creating new vips image from file: ' . $this->source
|
||||
);*/
|
||||
$message = /** @scrutinizer ignore-call */ vips_error_buffer();
|
||||
throw new ConversionFailedException($message);
|
||||
}
|
||||
|
||||
if (!is_array($result)) {
|
||||
throw new ConversionFailedException(
|
||||
'vips_image_new_from_file did not return an array, which we expected'
|
||||
);
|
||||
}
|
||||
|
||||
if (count($result) != 1) {
|
||||
throw new ConversionFailedException(
|
||||
'vips_image_new_from_file did not return an array of length 1 as we expected ' .
|
||||
'- length was: ' . count($result)
|
||||
);
|
||||
}
|
||||
|
||||
$im = array_shift($result);
|
||||
return $im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create parameters for webpsave
|
||||
*
|
||||
* @return array the parameters as an array
|
||||
*/
|
||||
private function createParamsForVipsWebPSave()
|
||||
{
|
||||
// webpsave options are described here:
|
||||
// https://libvips.github.io/libvips/API/current/VipsForeignSave.html#vips-webpsave
|
||||
// near_lossless option is described here: https://github.com/libvips/libvips/pull/430
|
||||
|
||||
// NOTE: When a new option becomes available, we MUST remember to add
|
||||
// it to the array of possibly unsupported options in webpsave() !
|
||||
$options = [
|
||||
"Q" => $this->getCalculatedQuality(),
|
||||
'lossless' => ($this->options['encoding'] == 'lossless'),
|
||||
'strip' => $this->options['metadata'] == 'none',
|
||||
];
|
||||
|
||||
// Only set the following options if they differ from the default of vipslib
|
||||
// This ensures we do not get warning if that property isn't supported
|
||||
if ($this->options['smart-subsample'] !== false) {
|
||||
// PS: The smart-subsample option is now deprecated, as it turned out
|
||||
// it was corresponding to the "sharp-yuv" option (see #280)
|
||||
$options['smart_subsample'] = $this->options['smart-subsample'];
|
||||
$this->logLn(
|
||||
'*Note: the "smart-subsample" option is now deprecated. It turned out it corresponded to ' .
|
||||
'the general option "sharp-yuv". You should use "sharp-yuv" instead.*'
|
||||
);
|
||||
}
|
||||
if ($this->options['sharp-yuv'] !== false) {
|
||||
$options['smart_subsample'] = $this->options['sharp-yuv'];
|
||||
}
|
||||
|
||||
if ($this->options['alpha-quality'] !== 100) {
|
||||
$options['alpha_q'] = $this->options['alpha-quality'];
|
||||
}
|
||||
|
||||
if (!is_null($this->options['preset']) && ($this->options['preset'] != 'none')) {
|
||||
// preset. 0:default, 1:picture, 2:photo, 3:drawing, 4:icon, 5:text, 6:last
|
||||
$options['preset'] = array_search(
|
||||
$this->options['preset'],
|
||||
['default', 'picture', 'photo', 'drawing', 'icon', 'text']
|
||||
);
|
||||
}
|
||||
if ($this->options['near-lossless'] !== 100) {
|
||||
if ($this->options['encoding'] == 'lossless') {
|
||||
// We only let near_lossless have effect when encoding is set to lossless
|
||||
// otherwise encoding=auto would not work as expected
|
||||
// Available in https://github.com/libvips/libvips/pull/430, merged 1 may 2016
|
||||
// seems it corresponds to release 8.4.2
|
||||
$options['near_lossless'] = true;
|
||||
|
||||
// In Vips, the near-lossless value is controlled by Q.
|
||||
// this differs from how it is done in cwebp, where it is an integer.
|
||||
// We have chosen same option syntax as cwebp
|
||||
$options['Q'] = $this->options['near-lossless'];
|
||||
}
|
||||
}
|
||||
if ($this->options['method'] !== 4) {
|
||||
$options['reduction_effort'] = $this->options['method'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save as webp, using vips extension.
|
||||
*
|
||||
* Tries to save image resource as webp, using the supplied options.
|
||||
* Vips fails when a parameter is not supported, but we detect this and unset that parameter and try again
|
||||
* (recursively call itself until there is no more of these kind of errors).
|
||||
*
|
||||
* @param resource $im A vips image resource to save
|
||||
* @throws ConversionFailedException if conversion fails.
|
||||
*/
|
||||
private function webpsave($im, $options)
|
||||
{
|
||||
/** @scrutinizer ignore-call */ vips_error_buffer(); // clear error buffer
|
||||
$result = /** @scrutinizer ignore-call */ vips_call('webpsave', $im, $this->destination, $options);
|
||||
|
||||
//trigger_error('test-warning', E_USER_WARNING);
|
||||
if ($result === -1) {
|
||||
$message = /** @scrutinizer ignore-call */ vips_error_buffer();
|
||||
|
||||
$nameOfPropertyNotFound = '';
|
||||
if (preg_match("#no property named .(.*).#", $message, $matches)) {
|
||||
$nameOfPropertyNotFound = $matches[1];
|
||||
} elseif (preg_match("#(.*)\\sunsupported$#", $message, $matches)) {
|
||||
// Actually, I am not quite sure if this ever happens.
|
||||
// I got a "near_lossless unsupported" error message in a build, but perhaps it rather a warning
|
||||
if (in_array($matches[1], [
|
||||
'lossless',
|
||||
'alpha_q',
|
||||
'near_lossless',
|
||||
'smart_subsample',
|
||||
'reduction_effort',
|
||||
'preset'
|
||||
])) {
|
||||
$nameOfPropertyNotFound = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($nameOfPropertyNotFound != '') {
|
||||
$msg = 'Note: Your version of vipslib does not support the "' .
|
||||
$nameOfPropertyNotFound . '" property';
|
||||
|
||||
switch ($nameOfPropertyNotFound) {
|
||||
case 'alpha_q':
|
||||
$msg .= ' (It was introduced in vips 8.4)';
|
||||
break;
|
||||
case 'near_lossless':
|
||||
$msg .= ' (It was introduced in vips 8.4)';
|
||||
break;
|
||||
case 'smart_subsample':
|
||||
$msg .= ' (its the vips equalent to the "sharp-yuv" option. It was introduced in vips 8.4)';
|
||||
break;
|
||||
case 'reduction_effort':
|
||||
$msg .= ' (its the vips equalent to the "method" option. It was introduced in vips 8.8.0)';
|
||||
break;
|
||||
case 'preset':
|
||||
$msg .= ' (It was introduced in vips 8.4)';
|
||||
break;
|
||||
}
|
||||
$msg .= '. The option is ignored.';
|
||||
|
||||
|
||||
$this->logLn($msg, 'bold');
|
||||
|
||||
unset($options[$nameOfPropertyNotFound]);
|
||||
$this->webpsave($im, $options);
|
||||
} else {
|
||||
throw new ConversionFailedException($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert, using vips extension.
|
||||
*
|
||||
* Tries to create image resource and save it as webp using the calculated options.
|
||||
* PS: The Vips "webpsave" call fails when a parameter is not supported, but our webpsave() method
|
||||
* detect this and unset that parameter and try again (repeat until success).
|
||||
*
|
||||
* @throws ConversionFailedException if conversion fails.
|
||||
*/
|
||||
protected function doActualConvert()
|
||||
{
|
||||
/*
|
||||
$im = \Jcupitt\Vips\Image::newFromFile($this->source);
|
||||
//$im->writeToFile(__DIR__ . '/images/small-vips.webp', ["Q" => 10]);
|
||||
|
||||
$im->webpsave($this->destination, [
|
||||
"Q" => 80,
|
||||
//'near_lossless' => true
|
||||
]);
|
||||
return;*/
|
||||
|
||||
$im = $this->createImageResource();
|
||||
$options = $this->createParamsForVipsWebPSave();
|
||||
$this->webpsave($im, $options);
|
||||
}
|
||||
}
|
||||
415
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Wpc.php
vendored
Normal file
415
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Converters/Wpc.php
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Converters;
|
||||
|
||||
use WebPConvert\Convert\Converters\AbstractConverter;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CloudConverterTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\CurlTrait;
|
||||
use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
use WebPConvert\Options\SensitiveStringOption;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
|
||||
/**
|
||||
* Convert images to webp using Wpc (a cloud converter based on WebP Convert).
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Wpc extends AbstractConverter
|
||||
{
|
||||
use CloudConverterTrait;
|
||||
use CurlTrait;
|
||||
use EncodingAutoTrait;
|
||||
|
||||
protected function getUnsupportedDefaultOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getUniqueOptions($imageType)
|
||||
{
|
||||
return OptionFactory::createOptions([
|
||||
['api-key', 'string', [
|
||||
'title' => 'API key',
|
||||
'description' => 'The API key is set up on the remote. Copy that.',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
'advanced' => false,
|
||||
'display' => "option('wpc-api-version') != 0"
|
||||
]
|
||||
]],
|
||||
['secret', 'string', [
|
||||
'title' => 'Secret',
|
||||
'description' => '',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
'advanced' => false,
|
||||
'display' => "option('wpc-api-version') == 0"
|
||||
]
|
||||
]],
|
||||
['api-url', 'string', [
|
||||
'title' => 'API url',
|
||||
'description' => 'The endpoint of the web service. Copy it from the remote setup',
|
||||
'default' => '',
|
||||
'sensitive' => true,
|
||||
'ui' => [
|
||||
'component' => 'password',
|
||||
'advanced' => false,
|
||||
]
|
||||
]],
|
||||
['api-version', 'int', [
|
||||
'title' => 'API version',
|
||||
'description' =>
|
||||
'Refers to the major version of Wpc. ' .
|
||||
'It is probably 2, as it is a long time since 2.0 was released',
|
||||
'default' => 2,
|
||||
'minimum' => 0,
|
||||
'maximum' => 2,
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'advanced' => false,
|
||||
'options' => [0, 1, 2],
|
||||
]
|
||||
]],
|
||||
['crypt-api-key-in-transfer', 'boolean', [
|
||||
'title' => 'Crypt API key in transfer',
|
||||
'description' =>
|
||||
'If checked, the api key will be crypted in requests. ' .
|
||||
'Crypting the api-key protects it from being stolen during transfer',
|
||||
'default' => false,
|
||||
'ui' => [
|
||||
'component' => 'checkbox',
|
||||
'advanced' => true,
|
||||
'display' => "option('wpc-api-version') >= 1"
|
||||
]
|
||||
]],
|
||||
]);
|
||||
|
||||
/*return [
|
||||
new SensitiveStringOption('api-key', ''),
|
||||
new SensitiveStringOption('secret', ''),
|
||||
new SensitiveStringOption('api-url', ''),
|
||||
new SensitiveStringOption('url', ''), // DO NOT USE. Only here to keep the protection
|
||||
new IntegerOption('api-version', 2, 0, 2),
|
||||
new BooleanOption('crypt-api-key-in-transfer', false) // new in api v.1
|
||||
];*/
|
||||
}
|
||||
|
||||
public function supportsLossless()
|
||||
{
|
||||
return ($this->options['api-version'] >= 2);
|
||||
}
|
||||
|
||||
public function passOnEncodingAuto()
|
||||
{
|
||||
// We could make this configurable. But I guess passing it on is always to be preferred
|
||||
// for api >= 2.
|
||||
return ($this->options['api-version'] >= 2);
|
||||
}
|
||||
|
||||
private static function createRandomSaltForBlowfish()
|
||||
{
|
||||
$salt = '';
|
||||
$validCharsForSalt = array_merge(
|
||||
range('A', 'Z'),
|
||||
range('a', 'z'),
|
||||
range('0', '9'),
|
||||
['.', '/']
|
||||
);
|
||||
|
||||
for ($i = 0; $i < 22; $i++) {
|
||||
$salt .= $validCharsForSalt[array_rand($validCharsForSalt)];
|
||||
}
|
||||
return $salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get api key from options or environment variable
|
||||
*
|
||||
* @return string api key or empty string if none is set
|
||||
*/
|
||||
private function getApiKey()
|
||||
{
|
||||
if ($this->options['api-version'] == 0) {
|
||||
if (!empty($this->options['secret'])) {
|
||||
return $this->options['secret'];
|
||||
}
|
||||
} elseif ($this->options['api-version'] >= 1) {
|
||||
if (!empty($this->options['api-key'])) {
|
||||
return $this->options['api-key'];
|
||||
}
|
||||
}
|
||||
if (defined('WEBPCONVERT_WPC_API_KEY')) {
|
||||
return constant('WEBPCONVERT_WPC_API_KEY');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_WPC_API_KEY'))) {
|
||||
return getenv('WEBPCONVERT_WPC_API_KEY');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url from options or environment variable
|
||||
*
|
||||
* @return string URL to WPC or empty string if none is set
|
||||
*/
|
||||
private function getApiUrl()
|
||||
{
|
||||
if (!empty($this->options['api-url'])) {
|
||||
return $this->options['api-url'];
|
||||
}
|
||||
if (defined('WEBPCONVERT_WPC_API_URL')) {
|
||||
return constant('WEBPCONVERT_WPC_API_URL');
|
||||
}
|
||||
if (!empty(getenv('WEBPCONVERT_WPC_API_URL'))) {
|
||||
return getenv('WEBPCONVERT_WPC_API_URL');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check operationality of Wpc converter.
|
||||
*
|
||||
* @throws SystemRequirementsNotMetException if system requirements are not met (curl)
|
||||
* @throws ConverterNotOperationalException if key is missing or invalid, or quota has exceeded
|
||||
*/
|
||||
public function checkOperationality()
|
||||
{
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$apiVersion = $options['api-version'];
|
||||
|
||||
if ($this->getApiUrl() == '') {
|
||||
if (isset($this->options['url']) && ($this->options['url'] != '')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'The "url" option has been renamed to "api-url" in webp-convert 2.0. ' .
|
||||
'You must change the configuration accordingly.'
|
||||
);
|
||||
}
|
||||
throw new ConverterNotOperationalException(
|
||||
'Missing URL. You must install Webp Convert Cloud Service on a server, ' .
|
||||
'or the WebP Express plugin for Wordpress - and supply the url.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($apiVersion == 0) {
|
||||
if (!empty($this->getApiKey())) {
|
||||
// if secret is set, we need md5() and md5_file() functions
|
||||
if (!function_exists('md5')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
|
||||
'contents. ' .
|
||||
'But the required md5() PHP function is not available.'
|
||||
);
|
||||
}
|
||||
if (!function_exists('md5_file')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
|
||||
'contents. But the required md5_file() PHP function is not available.'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($options['crypt-api-key-in-transfer']) {
|
||||
if (!function_exists('crypt')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Configured to crypt the api-key, but crypt() function is not available.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!defined('CRYPT_BLOWFISH')) {
|
||||
throw new ConverterNotOperationalException(
|
||||
'Configured to crypt the api-key. ' .
|
||||
'That requires Blowfish encryption, which is not available on your current setup.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for curl requirements
|
||||
$this->checkOperationalityForCurlTrait();
|
||||
}
|
||||
|
||||
/*
|
||||
public function checkConvertability()
|
||||
{
|
||||
// check upload limits
|
||||
$this->checkConvertabilityCloudConverterTrait();
|
||||
|
||||
// TODO: some from below can be moved up here
|
||||
}
|
||||
*/
|
||||
|
||||
private function createOptionsToSend()
|
||||
{
|
||||
$optionsToSend = $this->options;
|
||||
|
||||
if ($this->isQualityDetectionRequiredButFailing()) {
|
||||
// quality was set to "auto", but we could not meassure the quality of the jpeg locally
|
||||
// Ask the cloud service to do it, rather than using what we came up with.
|
||||
$optionsToSend['quality'] = 'auto';
|
||||
} else {
|
||||
$optionsToSend['quality'] = $this->getCalculatedQuality();
|
||||
}
|
||||
|
||||
// The following are unset for security reasons.
|
||||
unset($optionsToSend['converters']);
|
||||
unset($optionsToSend['secret']);
|
||||
unset($optionsToSend['api-key']);
|
||||
unset($optionsToSend['api-url']);
|
||||
|
||||
$apiVersion = $optionsToSend['api-version'];
|
||||
|
||||
if ($apiVersion == 1) {
|
||||
// Lossless can be "auto" in api 2, but in api 1 "auto" is not supported
|
||||
//unset($optionsToSend['lossless']);
|
||||
} elseif ($apiVersion == 2) {
|
||||
//unset($optionsToSend['png']);
|
||||
//unset($optionsToSend['jpeg']);
|
||||
|
||||
// The following are unset for security reasons.
|
||||
unset($optionsToSend['cwebp-command-line-options']);
|
||||
unset($optionsToSend['command-line-options']);
|
||||
}
|
||||
|
||||
return $optionsToSend;
|
||||
}
|
||||
|
||||
private function createPostData()
|
||||
{
|
||||
$options = $this->options;
|
||||
|
||||
$postData = [
|
||||
'file' => curl_file_create($this->source),
|
||||
'options' => json_encode($this->createOptionsToSend()),
|
||||
'servername' => (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')
|
||||
];
|
||||
|
||||
$apiVersion = $options['api-version'];
|
||||
|
||||
$apiKey = $this->getApiKey();
|
||||
|
||||
if ($apiVersion == 0) {
|
||||
$postData['hash'] = md5(md5_file($this->source) . $apiKey);
|
||||
} else {
|
||||
//$this->logLn('api key: ' . $apiKey);
|
||||
|
||||
if ($options['crypt-api-key-in-transfer']) {
|
||||
$salt = self::createRandomSaltForBlowfish();
|
||||
$postData['salt'] = $salt;
|
||||
|
||||
// Strip off the first 28 characters (the first 6 are always "$2y$10$". The next 22 is the salt)
|
||||
$postData['api-key-crypted'] = substr(crypt($apiKey, '$2y$10$' . $salt . '$'), 28);
|
||||
} else {
|
||||
$postData['api-key'] = $apiKey;
|
||||
}
|
||||
}
|
||||
return $postData;
|
||||
}
|
||||
|
||||
protected function doActualConvert()
|
||||
{
|
||||
$ch = self::initCurl();
|
||||
|
||||
//$this->logLn('api url: ' . $this->getApiUrl());
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $this->getApiUrl(),
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $this->createPostData(),
|
||||
CURLOPT_BINARYTRANSFER => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
$this->logLn('Curl error: ', 'bold');
|
||||
$this->logLn(curl_error($ch));
|
||||
throw new ConverterNotOperationalException('Curl error:');
|
||||
}
|
||||
|
||||
// Check if we got a 404
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($httpCode == 404) {
|
||||
curl_close($ch);
|
||||
throw new ConversionFailedException(
|
||||
'WPC was not found at the specified URL - we got a 404 response.'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for empty response
|
||||
if (empty($response)) {
|
||||
throw new ConversionFailedException(
|
||||
'Error: Unexpected result. We got nothing back. ' .
|
||||
'HTTP CODE: ' . $httpCode . '. ' .
|
||||
'Content type:' . curl_getinfo($ch, CURLINFO_CONTENT_TYPE)
|
||||
);
|
||||
};
|
||||
|
||||
// The WPC cloud service either returns an image or an error message
|
||||
// Images has application/octet-stream.
|
||||
// Verify that we got an image back.
|
||||
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
|
||||
curl_close($ch);
|
||||
|
||||
if (substr($response, 0, 1) == '{') {
|
||||
$responseObj = json_decode($response, true);
|
||||
if (isset($responseObj['errorCode'])) {
|
||||
switch ($responseObj['errorCode']) {
|
||||
case 0:
|
||||
throw new ConverterNotOperationalException(
|
||||
'There are problems with the server setup: "' .
|
||||
$responseObj['errorMessage'] . '"'
|
||||
);
|
||||
case 1:
|
||||
throw new InvalidApiKeyException(
|
||||
'Access denied. ' . $responseObj['errorMessage']
|
||||
);
|
||||
default:
|
||||
throw new ConversionFailedException(
|
||||
'Conversion failed: "' . $responseObj['errorMessage'] . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WPC 0.1 returns 'failed![error messag]' when conversion fails. Handle that.
|
||||
if (substr($response, 0, 7) == 'failed!') {
|
||||
throw new ConversionFailedException(
|
||||
'WPC failed converting image: "' . substr($response, 7) . '"'
|
||||
);
|
||||
}
|
||||
|
||||
$this->logLn('Bummer, we did not receive an image');
|
||||
$this->log('What we received starts with: "');
|
||||
$this->logLn(
|
||||
str_replace("\r", '', str_replace("\n", '', htmlentities(substr($response, 0, 400)))) . '..."'
|
||||
);
|
||||
|
||||
throw new ConversionFailedException('Unexpected result. We did not receive an image but something else.');
|
||||
//throw new ConverterNotOperationalException($response);
|
||||
}
|
||||
|
||||
$success = file_put_contents($this->destination, $response);
|
||||
curl_close($ch);
|
||||
|
||||
if (!$success) {
|
||||
throw new ConversionFailedException('Error saving file. Check file permissions');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class ConversionSkippedException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'The converter declined converting';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
|
||||
class InvalidApiKeyException extends ConverterNotOperationalException
|
||||
{
|
||||
public $description = 'The converter is not operational (access denied)';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
|
||||
|
||||
class SystemRequirementsNotMetException extends ConverterNotOperationalException
|
||||
{
|
||||
public $description = 'The converter is not operational (system requirements not met)';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class ConverterNotOperationalException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'The converter is not operational';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
|
||||
|
||||
class CreateDestinationFileException extends FileSystemProblemsException
|
||||
{
|
||||
public $description = 'The converter could not create destination file. Check file permisions!';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblems;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\FileSystemProblemsException;
|
||||
|
||||
class CreateDestinationFolderException extends FileSystemProblemsException
|
||||
{
|
||||
public $description = 'The converter could not create destination folder. Check file permisions!';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class FileSystemProblemsException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'Filesystem problems';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
|
||||
class ConverterNotFoundException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter does not exist.';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
|
||||
class InvalidImageTypeException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter does not handle the supplied image type';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInputException;
|
||||
|
||||
class TargetNotFoundException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter could not locate source file';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions\ConversionFailed;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class InvalidInputException extends ConversionFailedException
|
||||
{
|
||||
public $description = 'Invalid input';
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
/**
|
||||
* ConversionFailedException is the base exception in the hierarchy for conversion errors.
|
||||
*
|
||||
* Exception hierarchy from here:
|
||||
*
|
||||
* WebpConvertException
|
||||
* ConversionFailedException
|
||||
* ConversionSkippedException
|
||||
* ConverterNotOperationalException
|
||||
* InvalidApiKeyException
|
||||
* SystemRequirementsNotMetException
|
||||
* FileSystemProblemsException
|
||||
* CreateDestinationFileException
|
||||
* CreateDestinationFolderException
|
||||
* InvalidInputException
|
||||
* ConverterNotFoundException
|
||||
* InvalidImageTypeException
|
||||
* InvalidOptionValueException
|
||||
* TargetNotFoundException
|
||||
*/
|
||||
class ConversionFailedException extends WebPConvertException
|
||||
{
|
||||
//public $description = 'Conversion failed';
|
||||
public $description = '';
|
||||
}
|
||||
169
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Helpers/JpegQualityDetector.php
vendored
Normal file
169
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Helpers/JpegQualityDetector.php
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Helpers;
|
||||
|
||||
use ExecWithFallback\ExecWithFallback;
|
||||
|
||||
/**
|
||||
* Try to detect quality of a jpeg image using various tools.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class JpegQualityDetector
|
||||
{
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg using imagick extension.
|
||||
*
|
||||
* Note that the detection might fail for two different reasons:
|
||||
* 1) Imagick is not installed
|
||||
* 2) Imagick for some reason fails to detect quality for some images
|
||||
*
|
||||
* In both cases, null is returned.
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
private static function detectQualityOfJpgUsingImagick($filename)
|
||||
{
|
||||
if (extension_loaded('imagick') && class_exists('\\Imagick')) {
|
||||
try {
|
||||
$img = new \Imagick($filename);
|
||||
|
||||
// The required function is available as from PECL imagick v2.2.2
|
||||
if (method_exists($img, 'getImageCompressionQuality')) {
|
||||
$quality = $img->getImageCompressionQuality();
|
||||
if ($quality === 0) {
|
||||
// We have experienced that this Imagick method returns 0 for some images,
|
||||
// (even though the imagemagick binary is able to detect the quality)
|
||||
// ie "/test/images/quality-undetectable-with-imagick.jpg". See #208
|
||||
$quality = null;
|
||||
}
|
||||
return $quality;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Well well, it just didn't work out.
|
||||
// - But perhaps next method will work...
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg using imagick binary.
|
||||
*
|
||||
* Note that the detection might fail for three different reasons:
|
||||
* 1) exec function is not available
|
||||
* 2) the 'identify' command is not available on the system
|
||||
* 3) imagemagick for some reason fails to detect quality for some images
|
||||
*
|
||||
* In the first two cases, null is returned.
|
||||
* In the third case, 92 is returned. This is what imagemagick returns when it cannot detect the quality.
|
||||
* and unfortunately we cannot distinguish between the situation where the quality is undetectable
|
||||
* and the situation where the quality is actually 92 (at least, I have not found a way to do so)
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
private static function detectQualityOfJpgUsingImageMagick($filename)
|
||||
{
|
||||
if (ExecWithFallback::anyAvailable()) {
|
||||
// Try Imagick using exec, and routing stderr to stdout (the "2>$1" magic)
|
||||
|
||||
try {
|
||||
ExecWithFallback::exec(
|
||||
"identify -format '%Q' " . escapeshellarg($filename) . " 2>&1",
|
||||
$output,
|
||||
$returnCode
|
||||
);
|
||||
//echo 'out:' . print_r($output, true);
|
||||
if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
|
||||
return intval($output[0]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// its ok, there are other fish in the sea
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg using graphicsmagick binary.
|
||||
*
|
||||
* It seems that graphicsmagick is never able to detect the quality! - and always returns
|
||||
* the default quality, which is 75.
|
||||
* However, as this might be solved in future versions, the method might be useful one day.
|
||||
* But we treat "75" as a failure to detect and shall return null in that case.
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
private static function detectQualityOfJpgUsingGraphicsMagick($filename)
|
||||
{
|
||||
if (ExecWithFallback::anyAvailable()) {
|
||||
// Try GraphicsMagick
|
||||
try {
|
||||
ExecWithFallback::exec(
|
||||
"gm identify -format '%Q' " . escapeshellarg($filename) . " 2>&1",
|
||||
$output,
|
||||
$returnCode
|
||||
);
|
||||
if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) {
|
||||
$quality = intval($output[0]);
|
||||
|
||||
// It seems that graphicsmagick is (currently) never able to detect the quality!
|
||||
// - and always returns 75 as a fallback
|
||||
// We shall therefore treat 75 as a failure to detect. (#209)
|
||||
if ($quality == 75) {
|
||||
return null;
|
||||
}
|
||||
return $quality;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to detect quality of jpeg.
|
||||
*
|
||||
* Note: This method does not throw errors, but might dispatch warnings.
|
||||
* You can use the WarningsIntoExceptions class if it is critical to you that nothing gets "printed"
|
||||
*
|
||||
* @param string $filename A complete file path to file to be examined
|
||||
* @return int|null Quality, or null if it was not possible to detect quality
|
||||
*/
|
||||
public static function detectQualityOfJpg($filename)
|
||||
{
|
||||
|
||||
//trigger_error('warning test', E_USER_WARNING);
|
||||
|
||||
// Test that file exists in order not to break things.
|
||||
if (!file_exists($filename)) {
|
||||
// One could argue that it would be better to throw an Exception...?
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try Imagick extension, if available
|
||||
$quality = self::detectQualityOfJpgUsingImagick($filename);
|
||||
|
||||
if (is_null($quality)) {
|
||||
$quality = self::detectQualityOfJpgUsingImageMagick($filename);
|
||||
}
|
||||
|
||||
if (is_null($quality)) {
|
||||
$quality = self::detectQualityOfJpgUsingGraphicsMagick($filename);
|
||||
}
|
||||
|
||||
return $quality;
|
||||
}
|
||||
}
|
||||
70
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Helpers/PhpIniSizes.php
vendored
Normal file
70
modules/x13webp/vendor/rosell-dk/webp-convert/src/Convert/Helpers/PhpIniSizes.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Convert\Helpers;
|
||||
|
||||
/**
|
||||
* Get/parse shorthandsize strings from php.ini as bytes.
|
||||
*
|
||||
* Parse strings like "1k" into bytes (1024).
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class PhpIniSizes
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse a shordhandsize string as the ones returned by ini_get()
|
||||
*
|
||||
* Parse a shorthandsize string having the syntax allowed in php.ini and returned by ini_get().
|
||||
* Ie "1K" => 1024.
|
||||
* Strings without units are also accepted.
|
||||
* The shorthandbytes syntax is described here: https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
|
||||
*
|
||||
* @param string $shortHandSize A size string of the type returned by ini_get()
|
||||
* @return float|false The parsed size (beware: it is float, do not check high numbers for equality),
|
||||
* or false if parse error
|
||||
*/
|
||||
public static function parseShortHandSize($shortHandSize)
|
||||
{
|
||||
|
||||
$result = preg_match("#^\\s*(\\d+(?:\\.\\d+)?)([bkmgtpezy]?)\\s*$#i", $shortHandSize, $matches);
|
||||
if ($result !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Truncate, because that is what php does.
|
||||
$digitsValue = floor($matches[1]);
|
||||
|
||||
if ((count($matches) >= 3) && ($matches[2] != '')) {
|
||||
$unit = $matches[2];
|
||||
|
||||
// Find the position of the unit in the ordered string which is the power
|
||||
// of magnitude to multiply a kilobyte by.
|
||||
$position = stripos('bkmgtpezy', $unit);
|
||||
|
||||
return floatval($digitsValue * pow(1024, $position));
|
||||
} else {
|
||||
return $digitsValue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the size of an php.ini option.
|
||||
*
|
||||
* Calls ini_get() and parses the size to a number.
|
||||
* If the configuration option is null, does not exist, or cannot be parsed as a shorthandsize, false is returned
|
||||
*
|
||||
* @param string $varname The configuration option name.
|
||||
* @return float|false The parsed size or false if the configuration option does not exist
|
||||
*/
|
||||
public static function getIniBytes($iniVarName)
|
||||
{
|
||||
$iniVarValue = ini_get($iniVarName);
|
||||
if (($iniVarValue == '') || $iniVarValue === false) {
|
||||
return false;
|
||||
}
|
||||
return self::parseShortHandSize($iniVarValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions\InvalidInput;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
|
||||
class InvalidImageTypeException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter does not handle the supplied image type';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions\InvalidInput;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
|
||||
class TargetNotFoundException extends InvalidInputException
|
||||
{
|
||||
public $description = 'The converter could not locate source file';
|
||||
}
|
||||
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInputException.php
vendored
Normal file
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Exceptions/InvalidInputException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class InvalidInputException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Invalid input';
|
||||
}
|
||||
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Exceptions/SanityException.txt
vendored
Normal file
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Exceptions/SanityException.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class SanityException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Sanity check failed';
|
||||
}
|
||||
44
modules/x13webp/vendor/rosell-dk/webp-convert/src/Exceptions/WebPConvertException.php
vendored
Normal file
44
modules/x13webp/vendor/rosell-dk/webp-convert/src/Exceptions/WebPConvertException.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Exceptions;
|
||||
|
||||
/**
|
||||
* WebPConvertException is the base exception for all exceptions in this library.
|
||||
*
|
||||
* Note that the parameters for the constructor differs from that of the Exception class.
|
||||
* We do not use exception code here, but are instead allowing two version of the error message:
|
||||
* a short version and a long version.
|
||||
* The short version may not contain special characters or dynamic content.
|
||||
* The detailed version may.
|
||||
* If the detailed version isn't provided, getDetailedMessage will return the short version.
|
||||
*
|
||||
*/
|
||||
class WebPConvertException extends \Exception
|
||||
{
|
||||
public $description = '';
|
||||
protected $detailedMessage;
|
||||
protected $shortMessage;
|
||||
|
||||
public function getDetailedMessage()
|
||||
{
|
||||
return $this->detailedMessage;
|
||||
}
|
||||
|
||||
public function getShortMessage()
|
||||
{
|
||||
return $this->shortMessage;
|
||||
}
|
||||
|
||||
public function __construct($shortMessage = "", $detailedMessage = "", $previous = null)
|
||||
{
|
||||
$detailedMessage = ($detailedMessage != '') ? $detailedMessage : $shortMessage;
|
||||
$this->detailedMessage = $detailedMessage;
|
||||
$this->shortMessage = $shortMessage;
|
||||
|
||||
parent::__construct(
|
||||
$detailedMessage,
|
||||
0,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
||||
61
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/InputValidator.php
vendored
Normal file
61
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/InputValidator.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use WebPConvert\Helpers\MimeType;
|
||||
use WebPConvert\Helpers\PathChecker;
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
use WebPConvert\Exceptions\InvalidInput\InvalidImageTypeException;
|
||||
|
||||
/**
|
||||
* Functions for sanitizing.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.6
|
||||
*/
|
||||
class InputValidator
|
||||
{
|
||||
|
||||
private static $allowedMimeTypes = [
|
||||
'image/jpeg',
|
||||
'image/png'
|
||||
];
|
||||
|
||||
/**
|
||||
* Check mimetype and if file path is ok and exists
|
||||
*/
|
||||
public static function checkMimeType($filePath, $allowedMimeTypes = null)
|
||||
{
|
||||
if (is_null($allowedMimeTypes)) {
|
||||
$allowedMimeTypes = self::$allowedMimeTypes;
|
||||
}
|
||||
// the following also tests that file path is ok and file exists
|
||||
$fileMimeType = MimeType::getMimeTypeDetectionResult($filePath);
|
||||
|
||||
if (is_null($fileMimeType)) {
|
||||
throw new InvalidImageTypeException('Image type could not be detected');
|
||||
} elseif ($fileMimeType === false) {
|
||||
throw new InvalidImageTypeException('File seems not to be an image.');
|
||||
} elseif (!in_array($fileMimeType, $allowedMimeTypes)) {
|
||||
throw new InvalidImageTypeException('Unsupported mime type: ' . $fileMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkSource($source)
|
||||
{
|
||||
PathChecker::checkSourcePath($source);
|
||||
self::checkMimeType($source);
|
||||
}
|
||||
|
||||
public static function checkDestination($destination)
|
||||
{
|
||||
PathChecker::checkDestinationPath($destination);
|
||||
}
|
||||
|
||||
public static function checkSourceAndDestination($source, $destination)
|
||||
{
|
||||
self::checkSource($source);
|
||||
self::checkDestination($destination);
|
||||
}
|
||||
}
|
||||
40
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/MimeType.php
vendored
Normal file
40
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/MimeType.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use ImageMimeTypeGuesser\ImageMimeTypeGuesser;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Get MimeType, results cached by path.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.6
|
||||
*/
|
||||
class MimeType
|
||||
{
|
||||
private static $cachedDetections = [];
|
||||
|
||||
/**
|
||||
* Get mime type for image (best guess).
|
||||
*
|
||||
* It falls back to using file extension. If that fails too, false is returned
|
||||
*
|
||||
* @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
|
||||
* false (if it is not an image type that the server knowns about)
|
||||
* or null (if nothing can be determined)
|
||||
*/
|
||||
public static function getMimeTypeDetectionResult($absFilePath)
|
||||
{
|
||||
PathChecker::checkAbsolutePathAndExists($absFilePath);
|
||||
|
||||
if (isset(self::$cachedDetections[$absFilePath])) {
|
||||
return self::$cachedDetections[$absFilePath];
|
||||
}
|
||||
self::$cachedDetections[$absFilePath] = ImageMimeTypeGuesser::lenientGuess($absFilePath);
|
||||
return self::$cachedDetections[$absFilePath];
|
||||
}
|
||||
}
|
||||
115
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/PathChecker.php
vendored
Normal file
115
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/PathChecker.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use WebPConvert\Exceptions\InvalidInputException;
|
||||
use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
|
||||
|
||||
/**
|
||||
* Functions for sanitizing.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.6
|
||||
*/
|
||||
class PathChecker
|
||||
{
|
||||
|
||||
/**
|
||||
* Check absolute file path to prevent attacks.
|
||||
*
|
||||
* - Prevents non printable characters
|
||||
* - Prevents stream wrappers
|
||||
* - Prevents directory traversal
|
||||
*
|
||||
* Preventing non printable characters is especially done to prevent the NUL character, which can be used
|
||||
* to bypass other tests. See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
|
||||
*
|
||||
* Preventeng stream wrappers is especially done to protect against Phar Deserialization.
|
||||
* See https://blog.ripstech.com/2018/new-php-exploitation-technique/
|
||||
*
|
||||
* @param string $absFilePath
|
||||
* @return void
|
||||
*/
|
||||
public static function checkAbsolutePath($absFilePath, $text = 'file')
|
||||
{
|
||||
if (empty($absFilePath)) {
|
||||
throw new InvalidInputException('Empty filepath for ' . $text);
|
||||
}
|
||||
|
||||
// Prevent non printable characters
|
||||
/*
|
||||
if (!ctype_print($absFilePath)) {
|
||||
throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
|
||||
}*/
|
||||
|
||||
// Prevent control characters (at least the first 32 (#0 - #1f)
|
||||
if (preg_match('#[\x{0}-\x{1f}]#', $absFilePath)) {
|
||||
throw new InvalidInputException('Non-printable characters are not allowed');
|
||||
}
|
||||
|
||||
// Prevent directory traversal
|
||||
/* Disabled. We DO allow it again (#203)
|
||||
if (preg_match('#\.\.\/#', $absFilePath)) {
|
||||
throw new InvalidInputException('Directory traversal is not allowed in ' . $text . ' path');
|
||||
}*/
|
||||
|
||||
// Prevent stream wrappers ("phar://", "php://" and the like)
|
||||
// https://www.php.net/manual/en/wrappers.phar.php
|
||||
if (preg_match('#^\\w+://#', $absFilePath)) {
|
||||
throw new InvalidInputException('Stream wrappers are not allowed in ' . $text . ' path');
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkAbsolutePathAndExists($absFilePath, $text = 'file')
|
||||
{
|
||||
if (empty($absFilePath)) {
|
||||
throw new TargetNotFoundException($text . ' argument missing');
|
||||
}
|
||||
self::checkAbsolutePath($absFilePath, $text);
|
||||
if (@!file_exists($absFilePath)) {
|
||||
throw new TargetNotFoundException($text . ' file was not found');
|
||||
}
|
||||
if (@is_dir($absFilePath)) {
|
||||
throw new InvalidInputException($text . ' is a directory');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that source path is secure, file exists and it is not a dir.
|
||||
*
|
||||
* To also check mime type, use InputValidator::checkSource
|
||||
*/
|
||||
public static function checkSourcePath($source)
|
||||
{
|
||||
self::checkAbsolutePathAndExists($source, 'source');
|
||||
}
|
||||
|
||||
public static function checkDestinationPath($destination)
|
||||
{
|
||||
if (empty($destination)) {
|
||||
throw new InvalidInputException('Destination argument missing');
|
||||
}
|
||||
self::checkAbsolutePath($destination, 'destination');
|
||||
|
||||
if (!preg_match('#\.webp$#i', $destination)) {
|
||||
// Prevent overriding important files.
|
||||
// Overriding an .htaccess file would lay down the website.
|
||||
throw new InvalidInputException(
|
||||
'Destination file must end with ".webp". ' .
|
||||
'If you deliberately want to store the webp files with another extension, you must rename ' .
|
||||
'the file after successful conversion'
|
||||
);
|
||||
}
|
||||
|
||||
if (@is_dir($destination)) {
|
||||
throw new InvalidInputException('Destination is a directory');
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkSourceAndDestinationPaths($source, $destination)
|
||||
{
|
||||
self::checkSourcePath($source);
|
||||
self::checkDestinationPath($destination);
|
||||
}
|
||||
}
|
||||
30
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/Sanitize.php
vendored
Normal file
30
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/Sanitize.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
class Sanitize
|
||||
{
|
||||
|
||||
/**
|
||||
* The NUL character is a demon, because it can be used to bypass other tests
|
||||
* See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
|
||||
*
|
||||
* @param string $string string remove NUL characters in
|
||||
*/
|
||||
public static function removeNUL($string)
|
||||
{
|
||||
return str_replace(chr(0), '', $string);
|
||||
}
|
||||
|
||||
public static function removeStreamWrappers($string)
|
||||
{
|
||||
return preg_replace('#^\\w+://#', '', $string);
|
||||
}
|
||||
|
||||
public static function path($string)
|
||||
{
|
||||
$string = self::removeNUL($string);
|
||||
$string = self::removeStreamWrappers($string);
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
255
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/SanityCheck.txt
vendored
Normal file
255
modules/x13webp/vendor/rosell-dk/webp-convert/src/Helpers/SanityCheck.txt
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Helpers;
|
||||
|
||||
use WebPConvert\Helpers\Sanitize;
|
||||
use WebPConvert\Exceptions\SanityException;
|
||||
|
||||
class SanityCheck
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $input string to test for NUL char
|
||||
*/
|
||||
public static function mustBeString($input, $errorMsg = 'String expected')
|
||||
{
|
||||
if (gettype($input) !== 'string') {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* The NUL character is a demon, because it can be used to bypass other tests
|
||||
* See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
|
||||
*
|
||||
* @param string $input string to test for NUL char
|
||||
*/
|
||||
public static function noNUL($input, $errorMsg = 'NUL character is not allowed')
|
||||
{
|
||||
self::mustBeString($input);
|
||||
if (strpos($input, chr(0)) !== false) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent control chararters (#00 - #20).
|
||||
*
|
||||
* This prevents line feed, new line, tab, charater return, tab, ets.
|
||||
* https://www.rapidtables.com/code/text/ascii-table.html
|
||||
*
|
||||
* @param string $input string to test for control characters
|
||||
*/
|
||||
public static function noControlChars($input)
|
||||
{
|
||||
self::mustBeString($input);
|
||||
self::noNUL($input);
|
||||
if (preg_match('#[\x{0}-\x{1f}]#', $input)) {
|
||||
throw new SanityException('Control characters are not allowed');
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mixed $input something that may not be empty
|
||||
*/
|
||||
public static function notEmpty($input, $errorMsg = 'Must be non-empty')
|
||||
{
|
||||
if (empty($input)) {
|
||||
throw new SanityException($input);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function noDirectoryTraversal($input, $errorMsg = 'Directory traversal is not allowed')
|
||||
{
|
||||
self::mustBeString($input);
|
||||
self::noControlChars($input);
|
||||
if (preg_match('#\.\.\/#', $input)) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function noStreamWrappers($input, $errorMsg = 'Stream wrappers are not allowed')
|
||||
{
|
||||
self::mustBeString($input);
|
||||
self::noControlChars($input);
|
||||
|
||||
// Prevent stream wrappers ("phar://", "php://" and the like)
|
||||
// https://www.php.net/manual/en/wrappers.phar.php
|
||||
if (preg_match('#^\\w+://#', Sanitize::removeNUL($input))) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function path($input)
|
||||
{
|
||||
self::notEmpty($input);
|
||||
self::mustBeString($input);
|
||||
self::noControlChars($input);
|
||||
self::noDirectoryTraversal($input);
|
||||
self::noStreamWrappers($input);
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function pathWithoutDirectoryTraversal($input)
|
||||
{
|
||||
return self::path($input);
|
||||
}
|
||||
|
||||
public static function absPathMicrosoftStyle($input, $errorMsg = 'Not an fully qualified Windows path')
|
||||
{
|
||||
// On microsoft we allow [drive letter]:\
|
||||
if (!preg_match("#^[A-Z]:\\\\|/#", $input)) {
|
||||
throw new SanityException($errorMsg . ':' . $input);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function absPath($input, $errorMsg = 'Not an absolute path')
|
||||
{
|
||||
if ((strpos($input, '/') !== 0)) {
|
||||
|
||||
// Check if we are on Microsoft
|
||||
$onMicrosoft = false;
|
||||
if (isset($_SERVER['SERVER_SOFTWARE'])) {
|
||||
if (strpos(strtolower($_SERVER['SERVER_SOFTWARE']), 'microsoft') !== false) {
|
||||
$onMicrosoft = true;
|
||||
}
|
||||
}
|
||||
switch (PHP_OS) {
|
||||
case "WINNT":
|
||||
case "WIN32":
|
||||
case "INTERIX":
|
||||
case "UWIN":
|
||||
case "UWIN-W7":
|
||||
$onMicrosoft = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$onMicrosoft) {
|
||||
throw new SanityException($errorMsg . ':' . $input);
|
||||
}
|
||||
self::absPathMicrosoftStyle($input);
|
||||
|
||||
}
|
||||
return self::path($input);
|
||||
}
|
||||
|
||||
public static function pathBeginsWith($input, $beginsWith, $errorMsg = 'Path is outside allowed path')
|
||||
{
|
||||
self::path($input);
|
||||
if (!(strpos($input, $beginsWith) === 0)) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function findClosestExistingFolderSymLinksExpanded($input) {
|
||||
$levelsUp = 1;
|
||||
//echo 'input:' . $input;
|
||||
while (true) {
|
||||
$dir = dirname($input, $levelsUp);
|
||||
//echo 'dir:' . $dir . '<br>';
|
||||
$realPathResult = realpath($dir);
|
||||
if ($realPathResult !== false) {
|
||||
return $realPathResult;
|
||||
}
|
||||
if (($dir == '/') || (strlen($dir) < 4)) {
|
||||
return $dir;
|
||||
}
|
||||
$levelsUp++;
|
||||
}
|
||||
return '/';
|
||||
}
|
||||
|
||||
public static function pathBeginsWithSymLinksExpanded($input, $beginsWith, $errorMsg = 'Path is outside allowed path') {
|
||||
$closestExistingFolder = self::findClosestExistingFolderSymLinksExpanded($input);
|
||||
//throw new SanityException('hm.' . $input . ' : <br>' . $closestExistingFolder);
|
||||
self::pathBeginsWith($closestExistingFolder, $beginsWith, $errorMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function absPathExists($input, $errorMsg = 'Path does not exist')
|
||||
{
|
||||
self::absPath($input);
|
||||
if (@!file_exists($input)) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function absPathExistsAndIsDir(
|
||||
$input,
|
||||
$errorMsg = 'Path points to a file (it should point to a directory)'
|
||||
) {
|
||||
self::absPathExists($input);
|
||||
if (!is_dir($input)) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function absPathExistsAndIsFile(
|
||||
$input,
|
||||
$errorMsg = 'Path points to a directory (it should not do that)'
|
||||
) {
|
||||
self::absPathExists($input, 'File does not exist');
|
||||
if (@is_dir($input)) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function absPathExistsAndIsNotDir(
|
||||
$input,
|
||||
$errorMsg = 'Path points to a directory (it should point to a file)'
|
||||
) {
|
||||
self::absPathExistsAndIsFile($input, $errorMsg);
|
||||
return $input;
|
||||
}
|
||||
|
||||
|
||||
public static function pregMatch($pattern, $input, $errorMsg = 'Does not match expected pattern')
|
||||
{
|
||||
self::noNUL($input);
|
||||
self::mustBeString($input);
|
||||
if (!preg_match($pattern, $input)) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function isJSONArray($input, $errorMsg = 'Not a JSON array')
|
||||
{
|
||||
self::noNUL($input);
|
||||
self::mustBeString($input);
|
||||
self::notEmpty($input);
|
||||
if ((strpos($input, '[') !== 0) || (!is_array(json_decode($input)))) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function isJSONObject($input, $errorMsg = 'Not a JSON object')
|
||||
{
|
||||
self::noNUL($input);
|
||||
self::mustBeString($input);
|
||||
self::notEmpty($input);
|
||||
if ((strpos($input, '{') !== 0) || (!is_object(json_decode($input)))) {
|
||||
throw new SanityException($errorMsg);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
}
|
||||
44
modules/x13webp/vendor/rosell-dk/webp-convert/src/Loggers/BaseLogger.php
vendored
Normal file
44
modules/x13webp/vendor/rosell-dk/webp-convert/src/Loggers/BaseLogger.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Loggers;
|
||||
|
||||
/**
|
||||
* Base for all logger classes.
|
||||
*
|
||||
* WebPConvert can provide insights into the conversion process by means of accepting a logger which
|
||||
* extends this class.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
abstract class BaseLogger
|
||||
{
|
||||
/**
|
||||
* Write a message to the log
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
abstract public function log($msg, $style = '');
|
||||
|
||||
/**
|
||||
* Add new line to the log
|
||||
* @return void
|
||||
*/
|
||||
abstract public function ln();
|
||||
|
||||
/**
|
||||
* Write a line to the log
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
public function logLn($msg, $style = '')
|
||||
{
|
||||
$this->log($msg, $style);
|
||||
$this->ln();
|
||||
}
|
||||
}
|
||||
113
modules/x13webp/vendor/rosell-dk/webp-convert/src/Loggers/BufferLogger.php
vendored
Normal file
113
modules/x13webp/vendor/rosell-dk/webp-convert/src/Loggers/BufferLogger.php
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Loggers;
|
||||
|
||||
use WebPConvert\Loggers\BaseLogger;
|
||||
|
||||
/**
|
||||
* Collect the logging and retrieve it later in HTML or plain text format.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class BufferLogger extends BaseLogger
|
||||
{
|
||||
public $entries = array();
|
||||
|
||||
/**
|
||||
* Write a message to the buffer - all entries can later be retrieved with getText() or getHtlm().
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $style = '')
|
||||
{
|
||||
$this->entries[] = [$msg, $style];
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a new line to the buffer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ln()
|
||||
{
|
||||
$this->entries[] = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get everything logged - as HTML.
|
||||
*
|
||||
* @return string The log, formatted as HTML.
|
||||
*/
|
||||
public function getHtml()
|
||||
{
|
||||
$html = '';
|
||||
foreach ($this->entries as $entry) {
|
||||
if ($entry == '') {
|
||||
$html .= '<br>';
|
||||
} else {
|
||||
list($msg, $style) = $entry;
|
||||
$msg = htmlspecialchars($msg);
|
||||
if ($style == 'bold') {
|
||||
$html .= '<b>' . $msg . '</b>';
|
||||
} elseif ($style == 'italic') {
|
||||
$html .= '<i>' . $msg . '</i>';
|
||||
} else {
|
||||
$html .= $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get everything logged - as markdown.
|
||||
*
|
||||
* @return string The log, formatted as MarkDown.
|
||||
*/
|
||||
public function getMarkDown($newLineChar = "\n\r")
|
||||
{
|
||||
$md = '';
|
||||
foreach ($this->entries as $entry) {
|
||||
if ($entry == '') {
|
||||
$md .= $newLineChar;
|
||||
} else {
|
||||
list($msg, $style) = $entry;
|
||||
if ($style == 'bold') {
|
||||
$md .= '**' . $msg . '** ';
|
||||
} elseif ($style == 'italic') {
|
||||
$md .= '*' . $msg . '* ';
|
||||
} else {
|
||||
$md .= $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $md;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get everything logged - as plain text.
|
||||
*
|
||||
* @param string $newLineChar. The character used for new lines.
|
||||
* @return string The log, formatted as plain text.
|
||||
*/
|
||||
public function getText($newLineChar = ' ')
|
||||
{
|
||||
$text = '';
|
||||
foreach ($this->entries as $entry) {
|
||||
if ($entry == '') { // empty string means new line
|
||||
if (substr($text, -2) != '.' . $newLineChar) {
|
||||
$text .= '.' . $newLineChar;
|
||||
}
|
||||
} else {
|
||||
list($msg, $style) = $entry;
|
||||
$text .= $msg;
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
43
modules/x13webp/vendor/rosell-dk/webp-convert/src/Loggers/EchoLogger.php
vendored
Normal file
43
modules/x13webp/vendor/rosell-dk/webp-convert/src/Loggers/EchoLogger.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Loggers;
|
||||
|
||||
/**
|
||||
* Echo the logs immediately (in HTML)
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class EchoLogger extends BaseLogger
|
||||
{
|
||||
|
||||
/**
|
||||
* Handle log() by echoing the message.
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @param string $style style (null | bold | italic)
|
||||
* @return void
|
||||
*/
|
||||
public function log($msg, $style = '')
|
||||
{
|
||||
$msg = htmlspecialchars($msg);
|
||||
if ($style == 'bold') {
|
||||
echo '<b>' . $msg . '</b>';
|
||||
} elseif ($style == 'italic') {
|
||||
echo '<i>' . $msg . '</i>';
|
||||
} else {
|
||||
echo $msg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ln by echoing a <br> tag.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ln()
|
||||
{
|
||||
echo '<br>';
|
||||
}
|
||||
}
|
||||
41
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/ArrayOption.php
vendored
Normal file
41
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/ArrayOption.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ArrayOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'array';
|
||||
protected $schemaType = ['array'];
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('array');
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (count($this->getValue()) == 0) {
|
||||
return '(empty array)';
|
||||
} else {
|
||||
return parent::getValueForPrint();
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = false;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
30
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/BooleanOption.php
vendored
Normal file
30
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/BooleanOption.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Boolean option
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class BooleanOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'boolean';
|
||||
protected $schemaType = ['boolean'];
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('boolean');
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return ($this->getValue() === true ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class InvalidOptionTypeException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Invalid option type';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class InvalidOptionValueException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Invalid option value';
|
||||
}
|
||||
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/Exceptions/OptionNotFoundException.php
vendored
Normal file
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/Exceptions/OptionNotFoundException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class OptionNotFoundException extends WebPConvertException
|
||||
{
|
||||
public $description = '';
|
||||
}
|
||||
24
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/GhostOption.php
vendored
Normal file
24
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/GhostOption.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Ghost option
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class GhostOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'ghost';
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return '(not defined for this converter)';
|
||||
}
|
||||
}
|
||||
76
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/IntegerOption.php
vendored
Normal file
76
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/IntegerOption.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class IntegerOption extends Option
|
||||
{
|
||||
protected $typeId = 'int';
|
||||
protected $schemaType = ['integer'];
|
||||
protected $minValue;
|
||||
protected $maxValue;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $id id of the option
|
||||
* @param integer $defaultValue default value for the option
|
||||
* @throws InvalidOptionValueException if the default value cannot pass the check
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
|
||||
{
|
||||
$this->minValue = $minValue;
|
||||
$this->maxValue = $maxValue;
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
protected function checkMin()
|
||||
{
|
||||
if (!is_null($this->minValue) && $this->getValue() < $this->minValue) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"' . $this->id . '" option must be set to minimum ' . $this->minValue . '. ' .
|
||||
'It was however set to: ' . $this->getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkMax()
|
||||
{
|
||||
if (!is_null($this->maxValue) && $this->getValue() > $this->maxValue) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"' . $this->id . '" option must be set to max ' . $this->maxValue . '. ' .
|
||||
'It was however set to: ' . $this->getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkMinMax()
|
||||
{
|
||||
$this->checkMin();
|
||||
$this->checkMax();
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('integer');
|
||||
$this->checkMinMax();
|
||||
}
|
||||
|
||||
public function getSchema()
|
||||
{
|
||||
$obj = parent::getSchema();
|
||||
$obj['minimum'] = $this->minValue;
|
||||
$obj['maximum'] = $this->maxValue;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
50
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/IntegerOrNullOption.php
vendored
Normal file
50
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/IntegerOrNullOption.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class IntegerOrNullOption extends IntegerOption
|
||||
{
|
||||
protected $schemaType = ['integer', 'null'];
|
||||
|
||||
public function __construct($id, $defaultValue, $minValue = null, $maxValue = null)
|
||||
{
|
||||
parent::__construct($id, $defaultValue, $minValue, $maxValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkMinMax();
|
||||
|
||||
$valueType = gettype($this->getValue());
|
||||
if (!in_array($valueType, ['integer', 'NULL'])) {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "' . $this->id . '" option must be either integer or NULL. ' .
|
||||
'You however provided a value of type: ' . $valueType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (gettype($this->getValue() == 'NULL')) {
|
||||
return 'null (not set)';
|
||||
}
|
||||
return parent::getValueForPrint();
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
47
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/MetadataOption.php
vendored
Normal file
47
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/MetadataOption.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Metadata option. A Comma-separated list ('all', 'none', 'exif', 'icc', 'xmp')
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class MetadataOption extends StringOption
|
||||
{
|
||||
|
||||
protected $typeId = 'metadata';
|
||||
protected $schemaType = ['string'];
|
||||
|
||||
public function __construct($id, $defaultValue)
|
||||
{
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
parent::check();
|
||||
|
||||
$value = $this->getValue();
|
||||
|
||||
if (($value == 'all') || ($value == 'none')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (explode(',', $value) as $item) {
|
||||
if (!in_array($value, ['exif', 'icc', 'xmp'])) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"metadata" option must be "all", "none" or a comma-separated list of "exif", "icc" or "xmp". ' .
|
||||
'It was however set to: "' . $value . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//$this->checkType('string');
|
||||
}
|
||||
}
|
||||
254
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/Option.php
vendored
Normal file
254
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/Option.php
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* (base) option class.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Option
|
||||
{
|
||||
/** @var string The id of the option */
|
||||
protected $id;
|
||||
|
||||
/** @var mixed The default value of the option */
|
||||
protected $defaultValue;
|
||||
|
||||
/** @var mixed The value of the option */
|
||||
protected $value;
|
||||
|
||||
/** @var boolean Whether the value has been set (if not, getValue() will return the default value) */
|
||||
protected $isExplicitlySet = false;
|
||||
|
||||
/** @var string An option must supply a type id */
|
||||
protected $typeId;
|
||||
|
||||
/** @var array Type constraints for the value (JSON schema syntax) */
|
||||
protected $schemaType = [];
|
||||
|
||||
/** @var array|null Array of allowed values (JSON schema syntax) */
|
||||
protected $enum = null; //https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values
|
||||
|
||||
/** @var boolean Whether the option has been deprecated */
|
||||
protected $deprecated = false;
|
||||
|
||||
/** @var string Help text */
|
||||
protected $helpText = '';
|
||||
|
||||
/** @var array UI Def */
|
||||
protected $ui;
|
||||
|
||||
/** @var array|null Extra Schema Def (ie holding 'title', 'description' or other)*/
|
||||
protected $extraSchemaDefs;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $id id of the option
|
||||
* @param mixed $defaultValue default value for the option
|
||||
* @throws InvalidOptionValueException if the default value cannot pass the check
|
||||
* @throws InvalidOptionTypeException if the default value is wrong type
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($id, $defaultValue)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->defaultValue = $defaultValue;
|
||||
|
||||
// Check that default value is ok
|
||||
$this->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id.
|
||||
*
|
||||
* @return string The id of the option
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id.
|
||||
*
|
||||
* @param string $id The id of the option
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default value.
|
||||
*
|
||||
* @return mixed The default value for the option
|
||||
*/
|
||||
public function getDefaultValue()
|
||||
{
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get value, or default value if value has not been explicitly set.
|
||||
*
|
||||
* @return mixed The value/default value
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
if (!$this->isExplicitlySet) {
|
||||
return $this->defaultValue;
|
||||
} else {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get to know if value has been set.
|
||||
*
|
||||
* @return boolean Whether or not the value has been set explicitly
|
||||
*/
|
||||
public function isValueExplicitlySet()
|
||||
{
|
||||
return $this->isExplicitlySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value
|
||||
*
|
||||
* @param mixed $value The value
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->isExplicitlySet = true;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value is valid.
|
||||
*
|
||||
* This base class does no checking, but this method is overridden by most other options.
|
||||
* @return void
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpful function for checking type - used by subclasses.
|
||||
*
|
||||
* @param string $expectedType The expected type, ie 'string'
|
||||
* @throws InvalidOptionTypeException If the type is invalid
|
||||
* @return void
|
||||
*/
|
||||
protected function checkType($expectedType)
|
||||
{
|
||||
if (gettype($this->getValue()) != $expectedType) {
|
||||
throw new InvalidOptionTypeException(
|
||||
'The "' . $this->id . '" option must be a ' . $expectedType .
|
||||
' (you provided a ' . gettype($this->getValue()) . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function markDeprecated()
|
||||
{
|
||||
$this->deprecated = true;
|
||||
}
|
||||
|
||||
public function isDeprecated()
|
||||
{
|
||||
return $this->deprecated;
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return print_r($this->getValue(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set help text for the option
|
||||
*
|
||||
* @param string $helpText The help text
|
||||
* @return void
|
||||
*/
|
||||
public function setHelpText($helpText)
|
||||
{
|
||||
$this->helpText = $helpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get help text for the option
|
||||
*
|
||||
* @return string $helpText The help text
|
||||
*/
|
||||
public function getHelpText()
|
||||
{
|
||||
return $this->helpText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ui definition for the option
|
||||
*
|
||||
* @param array $ui The UI def
|
||||
* @return void
|
||||
*/
|
||||
public function setUI($ui)
|
||||
{
|
||||
$this->ui = $ui;
|
||||
}
|
||||
|
||||
public function setExtraSchemaDefs($def)
|
||||
{
|
||||
$this->extraSchemaDefs = $def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get ui definition for the option
|
||||
*
|
||||
* @return array $ui The UI def
|
||||
*/
|
||||
public function getUI()
|
||||
{
|
||||
return $this->ui;
|
||||
}
|
||||
|
||||
public function getSchema()
|
||||
{
|
||||
if (isset($this->extraSchemaDefs)) {
|
||||
$schema = $this->extraSchemaDefs;
|
||||
} else {
|
||||
$schema = [];
|
||||
}
|
||||
$schema['type'] = $this->schemaType;
|
||||
$schema['default'] = $this->defaultValue;
|
||||
if (!is_null($this->enum)) {
|
||||
$schema['enum'] = $this->enum;
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = [
|
||||
'id' => $this->id,
|
||||
'schema' => $this->getSchema(),
|
||||
'ui' => $this->ui,
|
||||
];
|
||||
if ($this->deprecated) {
|
||||
$obj['deprecated'] = true;
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
96
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/OptionFactory.php
vendored
Normal file
96
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/OptionFactory.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\ArrayOption;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\IntegerOption;
|
||||
use WebPConvert\Options\IntegerOrNullOption;
|
||||
use WebPConvert\Options\MetadataOption;
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\SensitiveStringOption;
|
||||
use WebPConvert\Options\QualityOption;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.7.0
|
||||
*/
|
||||
class OptionFactory
|
||||
{
|
||||
|
||||
public static function createOption($optionName, $optionType, $def)
|
||||
{
|
||||
$option = null;
|
||||
switch ($optionType) {
|
||||
case 'int':
|
||||
$minValue = (isset($def['minimum']) ? $def['minimum'] : null);
|
||||
$maxValue = (isset($def['maximum']) ? $def['maximum'] : null);
|
||||
unset($def['minimum']);
|
||||
unset($def['maximum']);
|
||||
if (isset($def['allow-null']) && $def['allow-null']) {
|
||||
$option = new IntegerOrNullOption($optionName, $def['default'], $minValue, $maxValue);
|
||||
} else {
|
||||
if ($optionName == 'quality') {
|
||||
$option = new QualityOption($optionName, $def['default']);
|
||||
} else {
|
||||
$option = new IntegerOption($optionName, $def['default'], $minValue, $maxValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
if ($optionName == 'metadata') {
|
||||
$option = new MetadataOption($optionName, $def['default']);
|
||||
} else {
|
||||
$enum = (isset($def['enum']) ? $def['enum'] : null);
|
||||
if (isset($def['sensitive']) && ($def['sensitive'] == true)) {
|
||||
unset($def['sensitive']);
|
||||
$option = new SensitiveStringOption($optionName, $def['default'], $enum);
|
||||
} else {
|
||||
$option = new StringOption($optionName, $def['default'], $enum);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
$option = new BooleanOption($optionName, $def['default']);
|
||||
break;
|
||||
|
||||
case 'array':
|
||||
if (isset($def['sensitive']) && ($def['sensitive'] == true)) {
|
||||
$option = new SensitiveArrayOption($optionName, $def['default']);
|
||||
} else {
|
||||
$option = new ArrayOption($optionName, $def['default']);
|
||||
}
|
||||
break;
|
||||
}
|
||||
unset($def['default']);
|
||||
|
||||
if (!is_null($option)) {
|
||||
if (isset($def['deprecated'])) {
|
||||
$option->markDeprecated();
|
||||
}
|
||||
if (isset($def['ui'])) {
|
||||
$option->setUI($def['ui']);
|
||||
unset($def['ui']);
|
||||
}
|
||||
}
|
||||
$option->setExtraSchemaDefs($def);
|
||||
return $option;
|
||||
}
|
||||
|
||||
public static function createOptions($def)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($def as $i => list($optionName, $optionType, $optionDef)) {
|
||||
$option = self::createOption($optionName, $optionType, $optionDef);
|
||||
if (!is_null($option)) {
|
||||
$result[] = $option;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
209
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/Options.php
vendored
Normal file
209
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/Options.php
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\OptionNotFoundException;
|
||||
|
||||
/**
|
||||
* Handles a collection of options.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Options
|
||||
{
|
||||
|
||||
/** @var array A map of options, keyed by their id */
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* Add option.
|
||||
*
|
||||
* @param Option $option The option object to add to collection.
|
||||
* @return void
|
||||
*/
|
||||
public function addOption($option)
|
||||
{
|
||||
$this->options[$option->getId()] = $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add options.
|
||||
*
|
||||
* Conveniently add several options in one call.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addOptions()
|
||||
{
|
||||
$options = func_get_args();
|
||||
foreach ($options as $option) {
|
||||
$this->addOption($option);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
In some years, we can use the splat instead (requires PHP 5.6):
|
||||
@param Option[] ...$options Array of options objects to add
|
||||
public function addOptions(...$options)
|
||||
{
|
||||
foreach ($options as $option) {
|
||||
$this->addOption($option);
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Set the value of an option.
|
||||
*
|
||||
* @param string $id Id of the option
|
||||
* @param mixed $value Value of the option
|
||||
* @return void
|
||||
*/
|
||||
public function setOption($id, $value)
|
||||
{
|
||||
if (!isset($this->options[$id])) {
|
||||
throw new OptionNotFoundException(
|
||||
'Could not set option. There is no option called "' . $id . '" in the collection.'
|
||||
);
|
||||
}
|
||||
$option = $this->options[$id];
|
||||
$option->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set option, or create a new, if no such option exists.
|
||||
*
|
||||
* @param string $id Id of option to set/create
|
||||
* @param mixed $value Value of option
|
||||
* @return void
|
||||
*/
|
||||
public function setOrCreateOption($id, $value)
|
||||
{
|
||||
if (!isset($this->options[$id])) {
|
||||
$newOption = new GhostOption($id, null);
|
||||
$newOption->setValue($value);
|
||||
//$newOption = new Option($id, $value);
|
||||
$this->addOption($newOption);
|
||||
} else {
|
||||
$this->setOption($id, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of an option in the collection - by id.
|
||||
*
|
||||
* @deprecated Use getOptionValue() instead
|
||||
* @param string $id Id of the option to get
|
||||
* @throws OptionNotFoundException if the option is not in the collection
|
||||
* @return mixed The value of the option
|
||||
*/
|
||||
public function getOption($id)
|
||||
{
|
||||
return $this->getOptionValue($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Option in the collection by id.
|
||||
*
|
||||
* @param string $id Id of the option to get
|
||||
* @throws OptionNotFoundException if the option is not in the collection
|
||||
* @return mixed The value of the option
|
||||
*/
|
||||
public function getOptionById($id)
|
||||
{
|
||||
if (!isset($this->options[$id])) {
|
||||
throw new OptionNotFoundException(
|
||||
'There is no option called "' . $id . '" in the collection.'
|
||||
);
|
||||
}
|
||||
return $this->options[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of an option in the collection - by id.
|
||||
*
|
||||
* @param string $id Id of the option to get
|
||||
* @throws OptionNotFoundException if the option is not in the collection
|
||||
* @return mixed The value of the option
|
||||
*/
|
||||
public function getOptionValue($id)
|
||||
{
|
||||
$option = $this->getOptionById($id);
|
||||
return $option->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return map of Option objects.
|
||||
*
|
||||
* @return array map of option objects
|
||||
*/
|
||||
public function getOptionsMap()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return flat associative array of options (simple objects).
|
||||
*
|
||||
* @return array associative array of options
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->options as $id => $option) {
|
||||
$values[$id] = $option->getValue();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all options in the collection.
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
foreach ($this->options as $id => $option) {
|
||||
$option->check();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set help texts on multiple options
|
||||
*
|
||||
* @param array $helpTexts Hash of helptexts indexed by option id
|
||||
*/
|
||||
public function setHelpTexts($helpTexts)
|
||||
{
|
||||
foreach ($this->options as $option) {
|
||||
if (array_key_exists($option->getId(), $helpTexts)) {
|
||||
$option->setHelpText($helpTexts[$option->getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ui definitions on multiple options
|
||||
*
|
||||
* @param array $uis Hash of ui definitions indexed by option id
|
||||
*/
|
||||
public function setUI($uis)
|
||||
{
|
||||
foreach ($this->options as $option) {
|
||||
if (array_key_exists($option->getId(), $uis)) {
|
||||
$option->setUI($uis[$option->getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefinitions($deprecatedToo = false)
|
||||
{
|
||||
$defs = [];
|
||||
foreach ($this->options as $option) {
|
||||
if ($deprecatedToo || !($option->isDeprecated())) {
|
||||
$defs[] = $option->getDefinition();
|
||||
}
|
||||
}
|
||||
return $defs;
|
||||
}
|
||||
}
|
||||
59
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/QualityOption.php
vendored
Normal file
59
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/QualityOption.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Quality option.
|
||||
*
|
||||
* Quality can be a number between 0-100 or "auto"
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class QualityOption extends Option
|
||||
{
|
||||
protected $typeId = 'int';
|
||||
protected $schemaType = ['integer', 'string'];
|
||||
|
||||
public function __construct($id, $defaultValue)
|
||||
{
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$value = $this->getValue();
|
||||
if (gettype($value) == 'string') {
|
||||
if ($value != 'auto') {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "quality" option must be either "auto" or a number between 0-100. ' .
|
||||
'A string, different from "auto" was given'
|
||||
);
|
||||
}
|
||||
} elseif (gettype($value) == 'integer') {
|
||||
if (($value < 0) || ($value > 100)) {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "quality" option must be either "auto" or a number between 0-100. ' .
|
||||
'The number you provided (' . strval($value) . ') is out of range.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidOptionValueException(
|
||||
'The "quality" option must be either "auto" or an integer. ' .
|
||||
'You however provided a value of type: ' . gettype($value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (gettype($this->getValue()) == 'string') {
|
||||
return '"' . $this->getValue() . '"';
|
||||
}
|
||||
return $this->getValue();
|
||||
}
|
||||
}
|
||||
39
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/SensitiveArrayOption.php
vendored
Normal file
39
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/SensitiveArrayOption.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class SensitiveArrayOption extends ArrayOption
|
||||
{
|
||||
|
||||
public function check()
|
||||
{
|
||||
parent::check();
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (count($this->getValue()) == 0) {
|
||||
return '(empty array)';
|
||||
} else {
|
||||
return '(array of ' . count($this->getValue()) . ' items)';
|
||||
}
|
||||
//return '*****';
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = true;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
42
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/SensitiveStringOption.php
vendored
Normal file
42
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/SensitiveStringOption.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class SensitiveStringOption extends StringOption
|
||||
{
|
||||
|
||||
public function __construct($id, $defaultValue, $enum = null)
|
||||
{
|
||||
parent::__construct($id, $defaultValue, $enum);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
parent::check();
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
if (strlen($this->getValue()) == 0) {
|
||||
return '""';
|
||||
}
|
||||
return '*****';
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = true;
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
55
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/StringOption.php
vendored
Normal file
55
modules/x13webp/vendor/rosell-dk/webp-convert/src/Options/StringOption.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Options;
|
||||
|
||||
use WebPConvert\Options\Option;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
|
||||
/**
|
||||
* Abstract option class
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class StringOption extends Option
|
||||
{
|
||||
|
||||
protected $typeId = 'string';
|
||||
protected $enum;
|
||||
protected $schemaType = ['string'];
|
||||
|
||||
public function __construct($id, $defaultValue, $enum = null)
|
||||
{
|
||||
$this->enum = $enum;
|
||||
parent::__construct($id, $defaultValue);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->checkType('string');
|
||||
|
||||
if (!is_null($this->enum) && (!in_array($this->getValue(), $this->enum))) {
|
||||
throw new InvalidOptionValueException(
|
||||
'"' . $this->id . '" option must be on of these values: ' .
|
||||
'[' . implode(', ', $this->enum) . ']. ' .
|
||||
'It was however set to: "' . $this->getValue() . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getValueForPrint()
|
||||
{
|
||||
return '"' . $this->getValue() . '"';
|
||||
}
|
||||
|
||||
public function getDefinition()
|
||||
{
|
||||
$obj = parent::getDefinition();
|
||||
$obj['sensitive'] = false;
|
||||
if (!is_null($this->enum)) {
|
||||
$obj['options'] = $this->enum;
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/Exceptions/ServeFailedException.php
vendored
Normal file
10
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/Exceptions/ServeFailedException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert\Serve\Exceptions;
|
||||
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
class ServeFailedException extends WebPConvertException
|
||||
{
|
||||
public $description = 'Failed serving';
|
||||
}
|
||||
51
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/Header.php
vendored
Normal file
51
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/Header.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
/**
|
||||
* Add / Set HTTP header.
|
||||
*
|
||||
* This class does nothing more than adding two convenience functions for calling the "header" function.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Header
|
||||
{
|
||||
/**
|
||||
* Convenience function for adding header (append).
|
||||
*
|
||||
* @param string $header The header to add.
|
||||
* @return void
|
||||
*/
|
||||
public static function addHeader($header)
|
||||
{
|
||||
header($header, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for replacing header.
|
||||
*
|
||||
* @param string $header The header to set.
|
||||
* @return void
|
||||
*/
|
||||
public static function setHeader($header)
|
||||
{
|
||||
header($header, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add log header and optionally send it to a logger as well.
|
||||
*
|
||||
* @param string $msg Message to add to "X-WebP-Convert-Log" header
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
* @return void
|
||||
*/
|
||||
public static function addLogHeader($msg, $logger = null)
|
||||
{
|
||||
self::addHeader('X-WebP-Convert-Log: ' . $msg);
|
||||
if (!is_null($logger)) {
|
||||
$logger->logLn($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/Report.php
vendored
Normal file
54
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/Report.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Loggers\EchoLogger;
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
/**
|
||||
* Class for generating a HTML report of converting an image.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class Report
|
||||
{
|
||||
public static function convertAndReport($source, $destination, $options)
|
||||
{
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<style>td {vertical-align: top} table {color: #666}</style>
|
||||
<script>
|
||||
function showOptions(elToHide) {
|
||||
document.getElementById('options').style.display='block';
|
||||
elToHide.style.display='none';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr><td><i>source:</i></td><td><?php echo htmlentities($source) ?></td></tr>
|
||||
<tr><td><i>destination:</i></td><td><?php echo htmlentities($destination) ?><td></tr>
|
||||
</table>
|
||||
<br>
|
||||
<?php
|
||||
try {
|
||||
$echoLogger = new EchoLogger();
|
||||
$options['log-call-arguments'] = true;
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $echoLogger);
|
||||
} catch (\Exception $e) {
|
||||
$msg = $e->getMessage();
|
||||
echo '<b>' . $msg . '</b>';
|
||||
|
||||
//echo '<p>Rethrowing exception for your convenience</p>';
|
||||
//throw ($e);
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
216
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebP.php
vendored
Normal file
216
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebP.php
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Helpers\MimeType;
|
||||
use WebPConvert\Helpers\PathChecker;
|
||||
use WebPConvert\Serve\Exceptions\ServeFailedException;
|
||||
use WebPConvert\Serve\Header;
|
||||
use WebPConvert\Serve\Report;
|
||||
use WebPConvert\Serve\ServeFile;
|
||||
use WebPConvert\Options\ArrayOption;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\SensitiveArrayOption;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionTypeException;
|
||||
use WebPConvert\Options\Exceptions\InvalidOptionValueException;
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
/**
|
||||
* Serve a converted webp image.
|
||||
*
|
||||
* The webp that is served might end up being one of these:
|
||||
* - a fresh convertion
|
||||
* - the destionation
|
||||
* - the original
|
||||
*
|
||||
* Exactly which is a decision based upon options, file sizes and file modification dates
|
||||
* (see the serve method of this class for details)
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ServeConvertedWebP
|
||||
{
|
||||
|
||||
/**
|
||||
* Process options.
|
||||
*
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
|
||||
* @param array $options
|
||||
*/
|
||||
private static function processOptions($options)
|
||||
{
|
||||
$options2 = new Options();
|
||||
$options2->addOptions(
|
||||
new BooleanOption('reconvert', false),
|
||||
new BooleanOption('serve-original', false),
|
||||
new BooleanOption('show-report', false),
|
||||
new BooleanOption('suppress-warnings', true),
|
||||
new BooleanOption('redirect-to-self-instead-of-serving', false),
|
||||
new ArrayOption('serve-image', []),
|
||||
new SensitiveArrayOption('convert', [])
|
||||
);
|
||||
foreach ($options as $optionId => $optionValue) {
|
||||
$options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options2->check();
|
||||
return $options2->getOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve original file (source).
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param array $serveImageOptions (optional) options for serving an image
|
||||
* Supported options:
|
||||
* - All options supported by ServeFile::serve()
|
||||
* @throws ServeFailedException if source is not an image or mime type cannot be determined
|
||||
* @return void
|
||||
*/
|
||||
public static function serveOriginal($source, $serveImageOptions = [])
|
||||
{
|
||||
// PS: We do not use InputValidator::checkSource($source) because we want to be
|
||||
// a bit more lenient here and allow any image to be served (even though ie webp does not
|
||||
// qualify for being used as a source when converting)
|
||||
|
||||
// Check that the filename is ok (no control chars, streamwrappers), and that the file exists
|
||||
// and is not a dir
|
||||
PathChecker::checkSourcePath($source);
|
||||
|
||||
$contentType = MimeType::getMimeTypeDetectionResult($source);
|
||||
if (is_null($contentType)) {
|
||||
throw new ServeFailedException('Rejecting to serve original (mime type cannot be determined)');
|
||||
} elseif ($contentType === false) {
|
||||
throw new ServeFailedException('Rejecting to serve original (it is not an image)');
|
||||
} else {
|
||||
ServeFile::serve($source, $contentType, $serveImageOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve destination file.
|
||||
*
|
||||
* TODO: SHould this really be public?
|
||||
*
|
||||
* @param string $destination path to destination file
|
||||
* @param array $serveImageOptions (optional) options for serving (such as which headers to add)
|
||||
* Supported options:
|
||||
* - All options supported by ServeFile::serve()
|
||||
* @return void
|
||||
*/
|
||||
public static function serveDestination($destination, $serveImageOptions = [])
|
||||
{
|
||||
InputValidator::checkDestination($destination);
|
||||
ServeFile::serve($destination, 'image/webp', $serveImageOptions);
|
||||
}
|
||||
|
||||
|
||||
public static function warningHandler()
|
||||
{
|
||||
// do nothing! - as we do not return anything, the warning is suppressed
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve converted webp.
|
||||
*
|
||||
* Serve a converted webp. If a file already exists at the destination, that is served (unless it is
|
||||
* older than the source - in that case a fresh conversion will be made, or the file at the destination
|
||||
* is larger than the source - in that case the source is served). Some options may alter this logic.
|
||||
* In case no file exists at the destination, a fresh conversion is made and served.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting
|
||||
* Supported options:
|
||||
* 'show-report' => (boolean) If true, the decision will always be 'report'
|
||||
* 'serve-original' => (boolean) If true, the decision will be 'source' (unless above option is set)
|
||||
* 'reconvert ' => (boolean) If true, the decision will be 'fresh-conversion' (unless one of the
|
||||
* above options is set)
|
||||
* - All options supported by WebPConvert::convert()
|
||||
* - All options supported by ServeFile::serve()
|
||||
* @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
|
||||
*
|
||||
* @throws \WebPConvert\Exceptions\WebPConvertException If something went wrong.
|
||||
* @return void
|
||||
*/
|
||||
public static function serve($source, $destination, $options = [], $serveLogger = null, $convertLogger = null)
|
||||
{
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
|
||||
$options = self::processOptions($options);
|
||||
|
||||
if ($options['suppress-warnings']) {
|
||||
set_error_handler(
|
||||
array('\\WebPConvert\\Serve\\ServeConvertedWebP', "warningHandler"),
|
||||
E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//$options = array_merge(self::$defaultOptions, $options);
|
||||
|
||||
// Step 1: Is there a file at the destination? If not, trigger conversion
|
||||
// However 1: if "show-report" option is set, serve the report instead
|
||||
// However 2: "reconvert" option should also trigger conversion
|
||||
if ($options['show-report']) {
|
||||
Header::addLogHeader('Showing report', $serveLogger);
|
||||
Report::convertAndReport($source, $destination, $options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!@file_exists($destination)) {
|
||||
Header::addLogHeader('Converting (there were no file at destination)', $serveLogger);
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
|
||||
} elseif ($options['reconvert']) {
|
||||
Header::addLogHeader('Converting (told to reconvert)', $serveLogger);
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
|
||||
} else {
|
||||
// Step 2: Is the destination older than the source?
|
||||
// If yes, trigger conversion (deleting destination is implicit)
|
||||
$timestampSource = @filemtime($source);
|
||||
$timestampDestination = @filemtime($destination);
|
||||
if (($timestampSource !== false) &&
|
||||
($timestampDestination !== false) &&
|
||||
($timestampSource > $timestampDestination)) {
|
||||
Header::addLogHeader('Converting (destination was older than the source)', $serveLogger);
|
||||
WebPConvert::convert($source, $destination, $options['convert'], $convertLogger);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Serve the smallest file (destination or source)
|
||||
// However, first check if 'serve-original' is set
|
||||
if ($options['serve-original']) {
|
||||
Header::addLogHeader('Serving original (told to)', $serveLogger);
|
||||
self::serveOriginal($source, $options['serve-image']);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($options['redirect-to-self-instead-of-serving']) {
|
||||
Header::addLogHeader(
|
||||
'Redirecting to self! ' .
|
||||
'(hope you got redirection to existing webps set up, otherwise you will get a loop!)',
|
||||
$serveLogger
|
||||
);
|
||||
header('Location: ?fresh', 302);
|
||||
return;
|
||||
}
|
||||
|
||||
$filesizeDestination = @filesize($destination);
|
||||
$filesizeSource = @filesize($source);
|
||||
if (($filesizeSource !== false) &&
|
||||
($filesizeDestination !== false) &&
|
||||
($filesizeDestination > $filesizeSource)) {
|
||||
Header::addLogHeader('Serving original (it is smaller)', $serveLogger);
|
||||
self::serveOriginal($source, $options['serve-image']);
|
||||
return;
|
||||
}
|
||||
|
||||
Header::addLogHeader('Serving converted file', $serveLogger);
|
||||
self::serveDestination($destination, $options['serve-image']);
|
||||
}
|
||||
}
|
||||
160
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebPWithErrorHandling.php
vendored
Normal file
160
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/ServeConvertedWebPWithErrorHandling.php
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Serve\Header;
|
||||
use WebPConvert\Serve\Report;
|
||||
use WebPConvert\Serve\ServeConvertedWeb;
|
||||
use WebPConvert\Serve\Exceptions\ServeFailedException;
|
||||
use WebPConvert\Exceptions\WebPConvertException;
|
||||
|
||||
/**
|
||||
* Serve a converted webp image and handle errors.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ServeConvertedWebPWithErrorHandling
|
||||
{
|
||||
|
||||
/**
|
||||
* Process options.
|
||||
*
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
|
||||
* @param array $options
|
||||
*/
|
||||
private static function processOptions($options)
|
||||
{
|
||||
$options2 = new Options();
|
||||
$options2->addOptions(
|
||||
new StringOption('fail', 'original', ['original', '404', 'throw', 'report']),
|
||||
new StringOption('fail-when-fail-fails', 'throw', ['original', '404', 'throw', 'report'])
|
||||
);
|
||||
foreach ($options as $optionId => $optionValue) {
|
||||
$options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options2->check();
|
||||
return $options2->getOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add headers for preventing caching.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function addHeadersPreventingCaching()
|
||||
{
|
||||
Header::setHeader("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
||||
Header::addHeader("Cache-Control: post-check=0, pre-check=0");
|
||||
Header::setHeader("Pragma: no-cache");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform fail action.
|
||||
*
|
||||
* @param string $fail Action to perform (original | 404 | report)
|
||||
* @param string $failIfFailFails Action to perform if $fail action fails
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting
|
||||
* @param \Exception $e exception that was thrown when trying to serve
|
||||
* @param string $serveClass (optional) Full class name to a class that has a serveOriginal() method
|
||||
* @return void
|
||||
*/
|
||||
public static function performFailAction($fail, $failIfFailFails, $source, $destination, $options, $e, $serveClass)
|
||||
{
|
||||
self::addHeadersPreventingCaching();
|
||||
|
||||
Header::addLogHeader('Performing fail action: ' . $fail);
|
||||
|
||||
switch ($fail) {
|
||||
case 'original':
|
||||
try {
|
||||
//ServeConvertedWebP::serveOriginal($source, $options);
|
||||
call_user_func($serveClass . '::serveOriginal', $source, $options);
|
||||
} catch (\Exception $e) {
|
||||
self::performFailAction($failIfFailFails, '404', $source, $destination, $options, $e, $serveClass);
|
||||
}
|
||||
break;
|
||||
|
||||
case '404':
|
||||
$protocol = isset($_SERVER["SERVER_PROTOCOL"]) ? $_SERVER["SERVER_PROTOCOL"] : 'HTTP/1.0';
|
||||
Header::setHeader($protocol . " 404 Not Found");
|
||||
break;
|
||||
|
||||
case 'report':
|
||||
$options['show-report'] = true;
|
||||
Report::convertAndReport($source, $destination, $options);
|
||||
break;
|
||||
|
||||
case 'throw':
|
||||
throw $e;
|
||||
//break; commented out as phpstan complains. But do something else complain now?
|
||||
|
||||
case 'report-as-image':
|
||||
// TODO: Implement or discard ?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve webp image and handle errors as specified in the 'fail' option.
|
||||
*
|
||||
* This method basically wraps ServeConvertedWebP:serve in order to provide exception handling.
|
||||
* The error handling is set with the 'fail' option and can be either '404', 'original' or 'report'.
|
||||
* If set to '404', errors results in 404 Not Found headers being issued. If set to 'original', an
|
||||
* error results in the original being served.
|
||||
* Look up the ServeConvertedWebP:serve method to learn more.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting
|
||||
* Supported options:
|
||||
* - 'fail' => (string) Action to take on failure (404 | original | report | throw).
|
||||
* "404" or "throw" is recommended for development and "original" is recommended for production.
|
||||
* Default: 'original'.
|
||||
* - 'fail-when-fail-fails' => (string) Action to take if fail action also fails. Default: '404'.
|
||||
* - All options supported by WebPConvert::convert()
|
||||
* - All options supported by ServeFile::serve()
|
||||
* - All options supported by DecideWhatToServe::decide)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
|
||||
* @param string $serveClass (optional) Full class name to a class that has a serve() method and a
|
||||
* serveOriginal() method
|
||||
* @return void
|
||||
*/
|
||||
public static function serve(
|
||||
$source,
|
||||
$destination,
|
||||
$options = [],
|
||||
$serveLogger = null,
|
||||
$convertLogger = null,
|
||||
$serveClass = '\\WebPConvert\\Serve\\ServeConvertedWebP'
|
||||
) {
|
||||
|
||||
$options = self::processOptions($options);
|
||||
try {
|
||||
InputValidator::checkSourceAndDestination($source, $destination);
|
||||
//ServeConvertedWebP::serve($source, $destination, $options, $serveLogger);
|
||||
call_user_func($serveClass . '::serve', $source, $destination, $options, $serveLogger, $convertLogger);
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof \WebPConvert\Exceptions\WebPConvertException) {
|
||||
Header::addLogHeader($e->getShortMessage(), $serveLogger);
|
||||
}
|
||||
|
||||
self::performFailAction(
|
||||
$options['fail'],
|
||||
$options['fail-when-fail-fails'],
|
||||
$source,
|
||||
$destination,
|
||||
$options,
|
||||
$e,
|
||||
$serveClass
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/ServeFile.php
vendored
Normal file
133
modules/x13webp/vendor/rosell-dk/webp-convert/src/Serve/ServeFile.php
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace WebPConvert\Serve;
|
||||
|
||||
//use WebPConvert\Serve\Report;
|
||||
use WebPConvert\Helpers\InputValidator;
|
||||
use WebPConvert\Options\ArrayOption;
|
||||
use WebPConvert\Options\BooleanOption;
|
||||
use WebPConvert\Options\Options;
|
||||
use WebPConvert\Options\StringOption;
|
||||
use WebPConvert\Serve\Header;
|
||||
use WebPConvert\Serve\Exceptions\ServeFailedException;
|
||||
|
||||
/**
|
||||
* Serve a file (send to standard output)
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class ServeFile
|
||||
{
|
||||
|
||||
/**
|
||||
* Process options.
|
||||
*
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionTypeException If the type of an option is invalid
|
||||
* @throws \WebPConvert\Options\Exceptions\InvalidOptionValueException If the value of an option is invalid
|
||||
* @param array $options
|
||||
*/
|
||||
private static function processOptions($options)
|
||||
{
|
||||
$options2 = new Options();
|
||||
$options2->addOptions(
|
||||
new ArrayOption('headers', []),
|
||||
new StringOption('cache-control-header', 'public, max-age=31536000')
|
||||
);
|
||||
foreach ($options as $optionId => $optionValue) {
|
||||
$options2->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options2->check();
|
||||
$options = $options2->getOptions();
|
||||
|
||||
// headers option
|
||||
// --------------
|
||||
|
||||
$headerOptions = new Options();
|
||||
$headerOptions->addOptions(
|
||||
new BooleanOption('cache-control', false),
|
||||
new BooleanOption('content-length', true),
|
||||
new BooleanOption('content-type', true),
|
||||
new BooleanOption('expires', false),
|
||||
new BooleanOption('last-modified', true),
|
||||
new BooleanOption('vary-accept', false)
|
||||
);
|
||||
foreach ($options['headers'] as $optionId => $optionValue) {
|
||||
$headerOptions->setOrCreateOption($optionId, $optionValue);
|
||||
}
|
||||
$options['headers'] = $headerOptions->getOptions();
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve existing file.
|
||||
*
|
||||
* @param string $filename File to serve (absolute path)
|
||||
* @param string $contentType Content-type (used to set header).
|
||||
* Only used when the "set-content-type-header" option is set.
|
||||
* Set to ie "image/jpeg" for serving jpeg file.
|
||||
* @param array $options Array of named options (optional).
|
||||
* Supported options:
|
||||
* 'add-vary-accept-header' => (boolean) Whether to add *Vary: Accept* header or not. Default: true.
|
||||
* 'set-content-type-header' => (boolean) Whether to set *Content-Type* header or not. Default: true.
|
||||
* 'set-last-modified-header' => (boolean) Whether to set *Last-Modified* header or not. Default: true.
|
||||
* 'set-cache-control-header' => (boolean) Whether to set *Cache-Control* header or not. Default: true.
|
||||
* 'cache-control-header' => string Cache control header. Default: "public, max-age=86400"
|
||||
*
|
||||
* @throws ServeFailedException if serving failed
|
||||
* @return void
|
||||
*/
|
||||
public static function serve($filename, $contentType, $options = [])
|
||||
{
|
||||
// Check mimetype - this also checks that path is secure and file exists
|
||||
InputValidator::checkMimeType($filename, [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/gif'
|
||||
]);
|
||||
|
||||
/*
|
||||
if (!file_exists($filename)) {
|
||||
Header::addHeader('X-WebP-Convert-Error: Could not read file');
|
||||
throw new ServeFailedException('Could not read file');
|
||||
}*/
|
||||
|
||||
$options = self::processOptions($options);
|
||||
|
||||
if ($options['headers']['last-modified']) {
|
||||
Header::setHeader("Last-Modified: " . gmdate("D, d M Y H:i:s", @filemtime($filename)) . " GMT");
|
||||
}
|
||||
|
||||
if ($options['headers']['content-type']) {
|
||||
Header::setHeader('Content-Type: ' . $contentType);
|
||||
}
|
||||
|
||||
if ($options['headers']['vary-accept']) {
|
||||
Header::addHeader('Vary: Accept');
|
||||
}
|
||||
|
||||
if (!empty($options['cache-control-header'])) {
|
||||
if ($options['headers']['cache-control']) {
|
||||
Header::setHeader('Cache-Control: ' . $options['cache-control-header']);
|
||||
}
|
||||
if ($options['headers']['expires']) {
|
||||
// Add exprires header too (#126)
|
||||
// Check string for something like this: max-age:86400
|
||||
if (preg_match('#max-age\\s*=\\s*(\\d*)#', $options['cache-control-header'], $matches)) {
|
||||
$seconds = $matches[1];
|
||||
Header::setHeader('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + intval($seconds)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['headers']['content-length']) {
|
||||
Header::setHeader('Content-Length: ' . filesize($filename));
|
||||
}
|
||||
|
||||
if (@readfile($filename) === false) {
|
||||
Header::addHeader('X-WebP-Convert-Error: Could not read file');
|
||||
throw new ServeFailedException('Could not read file');
|
||||
}
|
||||
}
|
||||
}
|
||||
159
modules/x13webp/vendor/rosell-dk/webp-convert/src/WebPConvert.php
vendored
Normal file
159
modules/x13webp/vendor/rosell-dk/webp-convert/src/WebPConvert.php
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace WebPConvert;
|
||||
|
||||
//use WebPConvert\Convert\Converters\ConverterHelper;
|
||||
use WebPConvert\Convert\Converters\Stack;
|
||||
//use WebPConvert\Serve\ServeExistingOrHandOver;
|
||||
use WebPConvert\Convert\ConverterFactory;
|
||||
use WebPConvert\Options\OptionFactory;
|
||||
use WebPConvert\Serve\ServeConvertedWebP;
|
||||
use WebPConvert\Serve\ServeConvertedWebPWithErrorHandling;
|
||||
|
||||
/**
|
||||
* Convert images to webp and/or serve them.
|
||||
*
|
||||
* This class is just a couple of convenience methods for doing conversion and/or
|
||||
* serving.
|
||||
*
|
||||
* @package WebPConvert
|
||||
* @author Bjørn Rosell <it@rosell.dk>
|
||||
* @since Class available since Release 2.0.0
|
||||
*/
|
||||
class WebPConvert
|
||||
{
|
||||
|
||||
/**
|
||||
* Convert jpeg or png into webp
|
||||
*
|
||||
* Convenience method for calling Stack::convert.
|
||||
*
|
||||
* @param string $source The image to convert (absolute,no backslashes)
|
||||
* Image must be jpeg or png.
|
||||
* @param string $destination Where to store the converted file (absolute path, no backslashes).
|
||||
* @param array $options (optional) Array of named options
|
||||
* The options are documented here:
|
||||
* https://github.com/rosell-dk/webp-convert/blob/master/docs/v2.0/converting/options.md
|
||||
* @param \WebPConvert\Loggers\BaseLogger $logger (optional)
|
||||
*
|
||||
* @throws \WebPConvert\Convert\Exceptions\ConversionFailedException in case conversion fails
|
||||
* @return void
|
||||
*/
|
||||
public static function convert($source, $destination, $options = [], $logger = null)
|
||||
{
|
||||
if (isset($options['converter'])) {
|
||||
$converter = $options['converter'];
|
||||
unset($options['converter']);
|
||||
$c = ConverterFactory::makeConverter($converter, $source, $destination, $options, $logger);
|
||||
$c->doConvert();
|
||||
} else {
|
||||
Stack::convert($source, $destination, $options, $logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve webp image, converting first if neccessary.
|
||||
*
|
||||
* If an image already exists, it will be served, unless it is older or larger than the source. (If it is larger,
|
||||
* the original is served, if it is older, the existing webp will be deleted and a fresh conversion will be made
|
||||
* and served). In case of error, the action indicated in the 'fail' option will be triggered (default is to serve
|
||||
* the original). Look up the ServeConvertedWebP:serve() and the ServeConvertedWebPWithErrorHandling::serve()
|
||||
* methods to learn more.
|
||||
*
|
||||
* @param string $source path to source file
|
||||
* @param string $destination path to destination
|
||||
* @param array $options (optional) options for serving/converting. The options are documented in the
|
||||
* ServeConvertedWebPWithErrorHandling::serve() method
|
||||
* @param \WebPConvert\Loggers\BaseLogger $serveLogger (optional)
|
||||
* @param \WebPConvert\Loggers\BaseLogger $convertLogger (optional)
|
||||
* @return void
|
||||
*/
|
||||
public static function serveConverted(
|
||||
$source,
|
||||
$destination,
|
||||
$options = [],
|
||||
$serveLogger = null,
|
||||
$convertLogger = null
|
||||
) {
|
||||
//return ServeExistingOrHandOver::serveConverted($source, $destination, $options);
|
||||
//if (isset($options['handle-errors']) && $options['handle-errors'] === true) {
|
||||
if (isset($options['fail']) && ($options['fail'] != 'throw')) {
|
||||
ServeConvertedWebPWithErrorHandling::serve($source, $destination, $options, $serveLogger, $convertLogger);
|
||||
} else {
|
||||
ServeConvertedWebP::serve($source, $destination, $options, $serveLogger, $convertLogger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all converters available in webp-convert.
|
||||
*
|
||||
* @return array Array of ids.
|
||||
*/
|
||||
public static function getConverterIds()
|
||||
{
|
||||
$all = Stack::getAvailableConverters();
|
||||
$all[] = 'stack';
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get option definitions for all converters
|
||||
*
|
||||
* Added in order to give GUI's a way to automatically adjust their setting screens.
|
||||
*
|
||||
* @param bool $filterOutOptionsWithoutUI If options without UI defined should be filtered out
|
||||
*
|
||||
* @return array Array of options definitions - ready to be json encoded, or whatever
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public static function getConverterOptionDefinitions($filterOutOptionsWithoutUI = true)
|
||||
{
|
||||
$converterIds = self::getConverterIds();
|
||||
$result = [];
|
||||
|
||||
$ewww = ConverterFactory::makeConverter('ewww', '', '');
|
||||
$result['general'] = $ewww->getGeneralOptionDefinitions($filterOutOptionsWithoutUI);
|
||||
|
||||
$generalOptionHash = [];
|
||||
$generalOptionIds = [];
|
||||
foreach ($result['general'] as &$option) {
|
||||
$generalOptionIds[] = $option['id'];
|
||||
$option['unsupportedBy'] = [];
|
||||
$generalOptionHash[$option['id']] = &$option;
|
||||
}
|
||||
//$result['general'] = $generalOptionIds;
|
||||
array_unshift($result['general'], OptionFactory::createOption('converter', 'string', [
|
||||
'title' => 'Converter',
|
||||
'description' => 'Conversion method. ' .
|
||||
"Cwebp and vips are best. " .
|
||||
'the *magick are nearly as good, but only recent versions supports near-lossless. ' .
|
||||
'gd is poor, as it does not support any webp options. ' .
|
||||
'For full discussion, check the guide',
|
||||
'default' => 'stack',
|
||||
'enum' => $converterIds,
|
||||
'ui' => [
|
||||
'component' => 'select',
|
||||
'links' => [
|
||||
[
|
||||
'Guide',
|
||||
'https://github.com/rosell-dk/webp-convert/blob/master/docs/v1.3/converting/converters.md'
|
||||
]
|
||||
],
|
||||
]
|
||||
])->getDefinition());
|
||||
|
||||
$supportedBy = [];
|
||||
$uniqueOptions = [];
|
||||
|
||||
foreach ($converterIds as $converterId) {
|
||||
$c = ConverterFactory::makeConverter($converterId, '', '');
|
||||
foreach ($c->getUnsupportedGeneralOptions() as $optionId) {
|
||||
$generalOptionHash[$optionId]['unsupportedBy'][] = $converterId;
|
||||
}
|
||||
$optionDefinitions = $c->getUniqueOptionDefinitions($filterOutOptionsWithoutUI);
|
||||
$uniqueOptions[$converterId] = $optionDefinitions;
|
||||
}
|
||||
$result['unique'] = $uniqueOptions;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user