diff options
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php')
-rw-r--r-- | vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php | 230 |
1 files changed, 181 insertions, 49 deletions
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php b/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php index d4ed3d0c9..a2757ac8c 100644 --- a/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php +++ b/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php @@ -2,9 +2,7 @@ /** * Class QROutputAbstract * - * @filesource QROutputAbstract.php * @created 09.12.2015 - * @package chillerlan\QRCode\Output * @author Smiley <smiley@chillerlan.net> * @copyright 2015 Smiley * @license MIT @@ -12,10 +10,10 @@ namespace chillerlan\QRCode\Output; -use chillerlan\QRCode\{Data\QRMatrix, QRCode}; +use chillerlan\QRCode\Data\QRMatrix; use chillerlan\Settings\SettingsContainerInterface; - -use function call_user_func_array, dirname, file_put_contents, get_called_class, in_array, is_writable, sprintf; +use Closure; +use function base64_encode, dirname, file_put_contents, is_writable, ksort, sprintf; /** * common output abstract @@ -25,30 +23,11 @@ abstract class QROutputAbstract implements QROutputInterface{ /** * the current size of the QR matrix * - * @see \chillerlan\QRCode\Data\QRMatrix::size() + * @see \chillerlan\QRCode\Data\QRMatrix::getSize() */ protected int $moduleCount; /** - * the current output mode - * - * @see \chillerlan\QRCode\QROptions::$outputType - */ - protected string $outputMode; - - /** - * the default output mode of the current output module - */ - protected string $defaultMode; - - /** - * the current scaling for a QR pixel - * - * @see \chillerlan\QRCode\QROptions::$scale - */ - protected int $scale; - - /** * the side length of the QR image (modules * scale) */ protected int $length; @@ -68,62 +47,215 @@ abstract class QROutputAbstract implements QROutputInterface{ */ protected SettingsContainerInterface $options; + /** @see \chillerlan\QRCode\QROptions::$scale */ + protected int $scale; + /** @see \chillerlan\QRCode\QROptions::$connectPaths */ + protected bool $connectPaths; + /** @see \chillerlan\QRCode\QROptions::$excludeFromConnect */ + protected array $excludeFromConnect; + /** @see \chillerlan\QRCode\QROptions::$eol */ + protected string $eol; + /** @see \chillerlan\QRCode\QROptions::$drawLightModules */ + protected bool $drawLightModules; + /** @see \chillerlan\QRCode\QROptions::$drawCircularModules */ + protected bool $drawCircularModules; + /** @see \chillerlan\QRCode\QROptions::$keepAsSquare */ + protected array $keepAsSquare; + /** @see \chillerlan\QRCode\QROptions::$circleRadius */ + protected float $circleRadius; + protected float $circleDiameter; + /** * QROutputAbstract constructor. */ public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ - $this->options = $options; - $this->matrix = $matrix; - $this->moduleCount = $this->matrix->size(); + $this->options = $options; + $this->matrix = $matrix; + + if($this->options->invertMatrix){ + $this->matrix->invert(); + } + + $this->copyVars(); + $this->setMatrixDimensions(); + $this->setModuleValues(); + } + + /** + * Creates copies of several QROptions values to avoid calling the magic getters + * in long loops for a significant performance increase. + * + * These variables are usually used in the "module" methods and are called up to 31329 times (at version 40). + */ + protected function copyVars():void{ + + $vars = [ + 'connectPaths', + 'excludeFromConnect', + 'eol', + 'drawLightModules', + 'drawCircularModules', + 'keepAsSquare', + 'circleRadius', + ]; + + foreach($vars as $property){ + $this->{$property} = $this->options->{$property}; + } + + $this->circleDiameter = ($this->circleRadius * 2); + } + + /** + * Sets/updates the matrix dimensions + * + * Call this method if you modify the matrix from within your custom module in case the dimensions have been changed + */ + protected function setMatrixDimensions():void{ + $this->moduleCount = $this->matrix->getSize(); $this->scale = $this->options->scale; - $this->length = $this->moduleCount * $this->scale; + $this->length = ($this->moduleCount * $this->scale); + } + + /** + * Returns a 2 element array with the current output width and height + * + * The type and units of the values depend on the output class. The default value is the current module count * scale. + */ + protected function getOutputDimensions():array{ + return [$this->length, $this->length]; + } - $class = get_called_class(); + /** + * Sets the initial module values + */ + protected function setModuleValues():void{ - if(isset(QRCode::OUTPUT_MODES[$class]) && in_array($this->options->outputType, QRCode::OUTPUT_MODES[$class])){ - $this->outputMode = $this->options->outputType; + // first fill the map with the default values + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $this->moduleValues[$M_TYPE] = $this->getDefaultModuleValue($defaultValue); } - $this->setModuleValues(); + // now loop over the options values to replace defaults and add extra values + foreach($this->options->moduleValues as $M_TYPE => $value){ + if($this::moduleValueIsValid($value)){ + $this->moduleValues[$M_TYPE] = $this->prepareModuleValue($value); + } + } + + } + + /** + * Prepares the value for the given input (return value depends on the output class) + * + * @param mixed $value + * + * @return mixed|null + */ + abstract protected function prepareModuleValue($value); + + /** + * Returns a default value for either dark or light modules (return value depends on the output class) + * + * @return mixed|null + */ + abstract protected function getDefaultModuleValue(bool $isDark); + + /** + * Returns the prepared value for the given $M_TYPE + * + * @return mixed return value depends on the output class + * @throws \chillerlan\QRCode\Output\QRCodeOutputException if $moduleValues[$M_TYPE] doesn't exist + */ + protected function getModuleValue(int $M_TYPE){ + + if(!isset($this->moduleValues[$M_TYPE])){ + throw new QRCodeOutputException(sprintf('$M_TYPE %012b not found in module values map', $M_TYPE)); + } + + return $this->moduleValues[$M_TYPE]; + } + + /** + * Returns the prepared module value at the given coordinate [$x, $y] (convenience) + * + * @return mixed|null + */ + protected function getModuleValueAt(int $x, int $y){ + return $this->getModuleValue($this->matrix->get($x, $y)); } /** - * Sets the initial module values (clean-up & defaults) + * Returns a base64 data URI for the given string and mime type */ - abstract protected function setModuleValues():void; + protected function toBase64DataURI(string $data, ?string $mime = null):string{ + return sprintf('data:%s;base64,%s', ($mime ?? $this::MIME_TYPE), base64_encode($data)); + } /** - * saves the qr data to a file + * Saves the qr $data to a $file. If $file is null, nothing happens. * * @see file_put_contents() - * @see \chillerlan\QRCode\QROptions::cachefile + * @see \chillerlan\QRCode\QROptions::$cachefile * * @throws \chillerlan\QRCode\Output\QRCodeOutputException */ - protected function saveToFile(string $data, string $file):bool{ + protected function saveToFile(string $data, ?string $file = null):void{ + + if($file === null){ + return; + } if(!is_writable(dirname($file))){ - throw new QRCodeOutputException(sprintf('Could not write data to cache file: %s', $file)); + throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s', $file)); } - return (bool)file_put_contents($file, $data); + if(file_put_contents($file, $data) === false){ + throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s (file_put_contents error)', $file)); + } } /** - * @inheritDoc + * collects the modules per QRMatrix::M_* type and runs a $transform function on each module and + * returns an array with the transformed modules + * + * The transform callback is called with the following parameters: + * + * $x - current column + * $y - current row + * $M_TYPE - field value + * $M_TYPE_LAYER - (possibly modified) field value that acts as layer id */ - public function dump(string $file = null){ - $file ??= $this->options->cachefile; + protected function collectModules(Closure $transform):array{ + $paths = []; - // call the built-in output method with the optional file path as parameter - // to make the called method aware if a cache file was given - $data = call_user_func_array([$this, $this->outputMode ?? $this->defaultMode], [$file]); + // collect the modules for each type + foreach($this->matrix->getMatrix() as $y => $row){ + foreach($row as $x => $M_TYPE){ + $M_TYPE_LAYER = $M_TYPE; - if($file !== null){ - $this->saveToFile($data, $file); + if($this->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->excludeFromConnect)){ + // to connect paths we'll redeclare the $M_TYPE_LAYER to data only + $M_TYPE_LAYER = QRMatrix::M_DATA; + + if($this->matrix->isDark($M_TYPE)){ + $M_TYPE_LAYER = QRMatrix::M_DATA_DARK; + } + } + + // collect the modules per $M_TYPE + $module = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER); + + if(!empty($module)){ + $paths[$M_TYPE_LAYER][] = $module; + } + } } - return $data; + // beautify output + ksort($paths); + + return $paths; } } |