diff options
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/Output')
8 files changed, 885 insertions, 0 deletions
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php new file mode 100644 index 000000000..639bdd111 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php @@ -0,0 +1,17 @@ +<?php +/** + * Class QRCodeOutputException + * + * @filesource QRCodeOutputException.php + * @created 09.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCodeException; + +class QRCodeOutputException extends QRCodeException{} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php new file mode 100644 index 000000000..a15ae9ff3 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php @@ -0,0 +1,113 @@ +<?php +/** + * Class QRFpdf + * + * https://github.com/chillerlan/php-qrcode/pull/49 + * + * @filesource QRFpdf.php + * @created 03.06.2020 + * @package chillerlan\QRCode\Output + * @author Maximilian Kresse + * + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; +use chillerlan\QRCode\QRCodeException; +use chillerlan\Settings\SettingsContainerInterface; +use FPDF; + +use function array_values, class_exists, count, is_array; + +/** + * QRFpdf output module (requires fpdf) + * + * @see https://github.com/Setasign/FPDF + * @see http://www.fpdf.org/ + */ +class QRFpdf extends QROutputAbstract{ + + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + + if(!class_exists(FPDF::class)){ + // @codeCoverageIgnoreStart + throw new QRCodeException( + 'The QRFpdf output requires FPDF as dependency but the class "\FPDF" couldn\'t be found.' + ); + // @codeCoverageIgnoreEnd + } + + parent::__construct($options, $matrix); + } + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_array($v) || count($v) < 3){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? [0, 0, 0] + : [255, 255, 255]; + } + else{ + $this->moduleValues[$M_TYPE] = array_values($v); + } + + } + + } + + /** + * @inheritDoc + * + * @return string|\FPDF + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + + $fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, [$this->length, $this->length]); + $fpdf->AddPage(); + + $prevColor = null; + + foreach($this->matrix->matrix() as $y => $row){ + + foreach($row as $x => $M_TYPE){ + /** @var int $M_TYPE */ + $color = $this->moduleValues[$M_TYPE]; + + if($prevColor === null || $prevColor !== $color){ + /** @phan-suppress-next-line PhanParamTooFewUnpack */ + $fpdf->SetFillColor(...$color); + $prevColor = $color; + } + + $fpdf->Rect($x * $this->scale, $y * $this->scale, 1 * $this->scale, 1 * $this->scale, 'F'); + } + + } + + if($this->options->returnResource){ + return $fpdf; + } + + $pdfData = $fpdf->Output('S'); + + if($file !== null){ + $this->saveToFile($pdfData, $file); + } + + if($this->options->imageBase64){ + $pdfData = sprintf('data:application/pdf;base64,%s', base64_encode($pdfData)); + } + + return $pdfData; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRImage.php b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php new file mode 100644 index 000000000..8f533d341 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php @@ -0,0 +1,217 @@ +<?php +/** + * Class QRImage + * + * @filesource QRImage.php + * @created 05.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; +use chillerlan\QRCode\{QRCode, QRCodeException}; +use chillerlan\Settings\SettingsContainerInterface; +use Exception; + +use function array_values, base64_encode, call_user_func, count, extension_loaded, imagecolorallocate, imagecolortransparent, + imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array, + is_array, ob_end_clean, ob_get_contents, ob_start, range, sprintf; + +/** + * Converts the matrix into GD images, raw or base64 output (requires ext-gd) + * + * @see http://php.net/manual/book.image.php + */ +class QRImage extends QROutputAbstract{ + + /** + * GD image types that support transparency + * + * @var string[] + */ + protected const TRANSPARENCY_TYPES = [ + QRCode::OUTPUT_IMAGE_PNG, + QRCode::OUTPUT_IMAGE_GIF, + ]; + + protected string $defaultMode = QRCode::OUTPUT_IMAGE_PNG; + + /** + * The GD image resource + * + * @see imagecreatetruecolor() + * @var resource|\GdImage + * + * @phan-suppress PhanUndeclaredTypeProperty + */ + protected $image; + + /** + * @inheritDoc + * + * @throws \chillerlan\QRCode\QRCodeException + */ + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + + if(!extension_loaded('gd')){ + throw new QRCodeException('ext-gd not loaded'); // @codeCoverageIgnore + } + + parent::__construct($options, $matrix); + } + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_array($v) || count($v) < 3){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? [0, 0, 0] + : [255, 255, 255]; + } + else{ + $this->moduleValues[$M_TYPE] = array_values($v); + } + + } + + } + + /** + * @inheritDoc + * + * @return string|resource|\GdImage + * + * @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + + $this->image = imagecreatetruecolor($this->length, $this->length); + + // avoid: Indirect modification of overloaded property $imageTransparencyBG has no effect + // https://stackoverflow.com/a/10455217 + $tbg = $this->options->imageTransparencyBG; + /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */ + $background = imagecolorallocate($this->image, ...$tbg); + + if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){ + imagecolortransparent($this->image, $background); + } + + imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $background); + + foreach($this->matrix->matrix() as $y => $row){ + foreach($row as $x => $M_TYPE){ + $this->setPixel($x, $y, $this->moduleValues[$M_TYPE]); + } + } + + if($this->options->returnResource){ + return $this->image; + } + + $imageData = $this->dumpImage(); + + if($file !== null){ + $this->saveToFile($imageData, $file); + } + + if($this->options->imageBase64){ + $imageData = sprintf('data:image/%s;base64,%s', $this->options->outputType, base64_encode($imageData)); + } + + return $imageData; + } + + /** + * Creates a single QR pixel with the given settings + */ + protected function setPixel(int $x, int $y, array $rgb):void{ + imagefilledrectangle( + $this->image, + $x * $this->scale, + $y * $this->scale, + ($x + 1) * $this->scale, + ($y + 1) * $this->scale, + /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */ + imagecolorallocate($this->image, ...$rgb) + ); + } + + /** + * Creates the final image by calling the desired GD output function + * + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + protected function dumpImage():string{ + ob_start(); + + try{ + call_user_func([$this, $this->outputMode ?? $this->defaultMode]); + } + // not going to cover edge cases + // @codeCoverageIgnoreStart + catch(Exception $e){ + throw new QRCodeOutputException($e->getMessage()); + } + // @codeCoverageIgnoreEnd + + $imageData = ob_get_contents(); + imagedestroy($this->image); + + ob_end_clean(); + + return $imageData; + } + + /** + * PNG output + * + * @return void + */ + protected function png():void{ + imagepng( + $this->image, + null, + in_array($this->options->pngCompression, range(-1, 9), true) + ? $this->options->pngCompression + : -1 + ); + } + + /** + * Jiff - like... JitHub! + * + * @return void + */ + protected function gif():void{ + imagegif($this->image); + } + + /** + * JPG output + * + * @return void + */ + protected function jpg():void{ + imagejpeg( + $this->image, + null, + in_array($this->options->jpegQuality, range(0, 100), true) + ? $this->options->jpegQuality + : 85 + ); + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php new file mode 100644 index 000000000..49516d30e --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php @@ -0,0 +1,119 @@ +<?php +/** + * Class QRImagick + * + * @filesource QRImagick.php + * @created 04.07.2018 + * @package chillerlan\QRCode\Output + * @author smiley <smiley@chillerlan.net> + * @copyright 2018 smiley + * @license MIT + * + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; +use chillerlan\QRCode\QRCodeException; +use chillerlan\Settings\SettingsContainerInterface; +use Imagick, ImagickDraw, ImagickPixel; + +use function extension_loaded, is_string; + +/** + * ImageMagick output module (requires ext-imagick) + * + * @see http://php.net/manual/book.imagick.php + * @see http://phpimagick.com + */ +class QRImagick extends QROutputAbstract{ + + protected Imagick $imagick; + + /** + * @inheritDoc + */ + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + + if(!extension_loaded('imagick')){ + throw new QRCodeException('ext-imagick not loaded'); // @codeCoverageIgnore + } + + parent::__construct($options, $matrix); + } + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $type => $defaultValue){ + $v = $this->options->moduleValues[$type] ?? null; + + if(!is_string($v)){ + $this->moduleValues[$type] = $defaultValue + ? new ImagickPixel($this->options->markupDark) + : new ImagickPixel($this->options->markupLight); + } + else{ + $this->moduleValues[$type] = new ImagickPixel($v); + } + } + } + + /** + * @inheritDoc + * + * @return string|\Imagick + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + $this->imagick = new Imagick; + + $this->imagick->newImage( + $this->length, + $this->length, + new ImagickPixel($this->options->imagickBG ?? 'transparent'), + $this->options->imagickFormat + ); + + $this->drawImage(); + + if($this->options->returnResource){ + return $this->imagick; + } + + $imageData = $this->imagick->getImageBlob(); + + if($file !== null){ + $this->saveToFile($imageData, $file); + } + + return $imageData; + } + + /** + * Creates the QR image via ImagickDraw + */ + protected function drawImage():void{ + $draw = new ImagickDraw; + + foreach($this->matrix->matrix() as $y => $row){ + foreach($row as $x => $M_TYPE){ + $draw->setStrokeColor($this->moduleValues[$M_TYPE]); + $draw->setFillColor($this->moduleValues[$M_TYPE]); + $draw->rectangle( + $x * $this->scale, + $y * $this->scale, + ($x + 1) * $this->scale, + ($y + 1) * $this->scale + ); + + } + } + + $this->imagick->drawImage($draw); + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php new file mode 100644 index 000000000..06d6e88cb --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php @@ -0,0 +1,160 @@ +<?php +/** + * Class QRMarkup + * + * @filesource QRMarkup.php + * @created 17.12.2016 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2016 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCode; + +use function is_string, sprintf, strip_tags, trim; + +/** + * Converts the matrix into markup types: HTML, SVG, ... + */ +class QRMarkup extends QROutputAbstract{ + + protected string $defaultMode = QRCode::OUTPUT_MARKUP_SVG; + + /** + * @see \sprintf() + */ + protected string $svgHeader = '<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" '. + 'style="width: 100%%; height: auto;" viewBox="0 0 %2$d %2$d">'; + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_string($v)){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? $this->options->markupDark + : $this->options->markupLight; + } + else{ + $this->moduleValues[$M_TYPE] = trim(strip_tags($v), '\'"'); + } + + } + + } + + /** + * HTML output + */ + protected function html(string $file = null):string{ + + $html = empty($this->options->cssClass) + ? '<div>' + : '<div class="'.$this->options->cssClass.'">'; + + $html .= $this->options->eol; + + foreach($this->matrix->matrix() as $row){ + $html .= '<div>'; + + foreach($row as $M_TYPE){ + $html .= '<span style="background: '.$this->moduleValues[$M_TYPE].';"></span>'; + } + + $html .= '</div>'.$this->options->eol; + } + + $html .= '</div>'.$this->options->eol; + + if($file !== null){ + return '<!DOCTYPE html>'. + '<head><meta charset="UTF-8"><title>QR Code</title></head>'. + '<body>'.$this->options->eol.$html.'</body>'; + } + + return $html; + } + + /** + * SVG output + * + * @see https://github.com/codemasher/php-qrcode/pull/5 + */ + protected function svg(string $file = null):string{ + $matrix = $this->matrix->matrix(); + + $svg = sprintf($this->svgHeader, $this->options->cssClass, $this->options->svgViewBoxSize ?? $this->moduleCount) + .$this->options->eol + .'<defs>'.$this->options->svgDefs.'</defs>' + .$this->options->eol; + + foreach($this->moduleValues as $M_TYPE => $value){ + $path = ''; + + foreach($matrix as $y => $row){ + //we'll combine active blocks within a single row as a lightweight compression technique + $start = null; + $count = 0; + + foreach($row as $x => $module){ + + if($module === $M_TYPE){ + $count++; + + if($start === null){ + $start = $x; + } + + if(isset($row[$x + 1])){ + continue; + } + } + + if($count > 0){ + $len = $count; + $start ??= 0; // avoid type coercion in sprintf() - phan happy + + $path .= sprintf('M%s %s h%s v1 h-%sZ ', $start, $y, $len, $len); + + // reset count + $count = 0; + $start = null; + } + + } + + } + + if(!empty($path)){ + $svg .= sprintf( + '<path class="qr-%s %s" stroke="transparent" fill="%s" fill-opacity="%s" d="%s" />', + $M_TYPE, $this->options->cssClass, $value, $this->options->svgOpacity, $path + ); + } + + } + + // close svg + $svg .= '</svg>'.$this->options->eol; + + // if saving to file, append the correct headers + if($file !== null){ + return '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'. + $this->options->eol.$svg; + } + + if($this->options->imageBase64){ + $svg = sprintf('data:image/svg+xml;base64,%s', base64_encode($svg)); + } + + return $svg; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php b/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php new file mode 100644 index 000000000..d4ed3d0c9 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php @@ -0,0 +1,129 @@ +<?php +/** + * Class QROutputAbstract + * + * @filesource QROutputAbstract.php + * @created 09.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\{Data\QRMatrix, QRCode}; +use chillerlan\Settings\SettingsContainerInterface; + +use function call_user_func_array, dirname, file_put_contents, get_called_class, in_array, is_writable, sprintf; + +/** + * common output abstract + */ +abstract class QROutputAbstract implements QROutputInterface{ + + /** + * the current size of the QR matrix + * + * @see \chillerlan\QRCode\Data\QRMatrix::size() + */ + 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; + + /** + * an (optional) array of color values for the several QR matrix parts + */ + protected array $moduleValues; + + /** + * the (filled) data matrix object + */ + protected QRMatrix $matrix; + + /** + * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions + */ + protected SettingsContainerInterface $options; + + /** + * QROutputAbstract constructor. + */ + public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ + $this->options = $options; + $this->matrix = $matrix; + $this->moduleCount = $this->matrix->size(); + $this->scale = $this->options->scale; + $this->length = $this->moduleCount * $this->scale; + + $class = get_called_class(); + + if(isset(QRCode::OUTPUT_MODES[$class]) && in_array($this->options->outputType, QRCode::OUTPUT_MODES[$class])){ + $this->outputMode = $this->options->outputType; + } + + $this->setModuleValues(); + } + + /** + * Sets the initial module values (clean-up & defaults) + */ + abstract protected function setModuleValues():void; + + /** + * saves the qr data to a file + * + * @see file_put_contents() + * @see \chillerlan\QRCode\QROptions::cachefile + * + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + protected function saveToFile(string $data, string $file):bool{ + + if(!is_writable(dirname($file))){ + throw new QRCodeOutputException(sprintf('Could not write data to cache file: %s', $file)); + } + + return (bool)file_put_contents($file, $data); + } + + /** + * @inheritDoc + */ + public function dump(string $file = null){ + $file ??= $this->options->cachefile; + + // 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]); + + if($file !== null){ + $this->saveToFile($data, $file); + } + + return $data; + } + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php b/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php new file mode 100644 index 000000000..b07b8e7a5 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php @@ -0,0 +1,54 @@ +<?php +/** + * Interface QROutputInterface, + * + * @filesource QROutputInterface.php + * @created 02.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\Data\QRMatrix; + +/** + * Converts the data matrix into readable output + */ +interface QROutputInterface{ + + const DEFAULT_MODULE_VALUES = [ + // light + QRMatrix::M_NULL => false, // 0 + QRMatrix::M_DATA => false, // 4 + QRMatrix::M_FINDER => false, // 6 + QRMatrix::M_SEPARATOR => false, // 8 + QRMatrix::M_ALIGNMENT => false, // 10 + QRMatrix::M_TIMING => false, // 12 + QRMatrix::M_FORMAT => false, // 14 + QRMatrix::M_VERSION => false, // 16 + QRMatrix::M_QUIETZONE => false, // 18 + QRMatrix::M_LOGO => false, // 20 + QRMatrix::M_TEST => false, // 255 + // dark + QRMatrix::M_DARKMODULE << 8 => true, // 512 + QRMatrix::M_DATA << 8 => true, // 1024 + QRMatrix::M_FINDER << 8 => true, // 1536 + QRMatrix::M_ALIGNMENT << 8 => true, // 2560 + QRMatrix::M_TIMING << 8 => true, // 3072 + QRMatrix::M_FORMAT << 8 => true, // 3584 + QRMatrix::M_VERSION << 8 => true, // 4096 + QRMatrix::M_FINDER_DOT << 8 => true, // 5632 + QRMatrix::M_TEST << 8 => true, // 65280 + ]; + + /** + * generates the output, optionally dumps it to a file, and returns it + * + * @return mixed + */ + public function dump(string $file = null); + +} diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRString.php b/vendor/chillerlan/php-qrcode/src/Output/QRString.php new file mode 100644 index 000000000..3ed5153e1 --- /dev/null +++ b/vendor/chillerlan/php-qrcode/src/Output/QRString.php @@ -0,0 +1,76 @@ +<?php +/** + * Class QRString + * + * @filesource QRString.php + * @created 05.12.2015 + * @package chillerlan\QRCode\Output + * @author Smiley <smiley@chillerlan.net> + * @copyright 2015 Smiley + * @license MIT + * + * @noinspection PhpUnusedParameterInspection + * @noinspection PhpComposerExtensionStubsInspection + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCode; + +use function implode, is_string, json_encode; + +/** + * Converts the matrix data into string types + */ +class QRString extends QROutputAbstract{ + + protected string $defaultMode = QRCode::OUTPUT_STRING_TEXT; + + /** + * @inheritDoc + */ + protected function setModuleValues():void{ + + foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ + $v = $this->options->moduleValues[$M_TYPE] ?? null; + + if(!is_string($v)){ + $this->moduleValues[$M_TYPE] = $defaultValue + ? $this->options->textDark + : $this->options->textLight; + } + else{ + $this->moduleValues[$M_TYPE] = $v; + } + + } + + } + + /** + * string output + */ + protected function text(string $file = null):string{ + $str = []; + + foreach($this->matrix->matrix() as $row){ + $r = []; + + foreach($row as $M_TYPE){ + $r[] = $this->moduleValues[$M_TYPE]; + } + + $str[] = implode('', $r); + } + + return implode($this->options->eol, $str); + } + + /** + * JSON output + */ + protected function json(string $file = null):string{ + return json_encode($this->matrix->matrix()); + } + +} |