aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/chillerlan/php-qrcode/src/Output
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/chillerlan/php-qrcode/src/Output')
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php9
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QREps.php173
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php150
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php400
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php33
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php33
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php40
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php33
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php33
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRImage.php210
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRImagick.php206
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php174
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php51
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRMarkupSVG.php200
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QROutputAbstract.php230
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php222
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRString.php91
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRStringJSON.php67
-rw-r--r--vendor/chillerlan/php-qrcode/src/Output/QRStringText.php76
19 files changed, 1915 insertions, 516 deletions
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php
index 639bdd111..bf30f1bb0 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRCodeOutputException.php
@@ -2,9 +2,7 @@
/**
* Class QRCodeOutputException
*
- * @filesource QRCodeOutputException.php
* @created 09.12.2015
- * @package chillerlan\QRCode\Output
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -14,4 +12,9 @@ namespace chillerlan\QRCode\Output;
use chillerlan\QRCode\QRCodeException;
-class QRCodeOutputException extends QRCodeException{}
+/**
+ * An exception container
+ */
+final class QRCodeOutputException extends QRCodeException{
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QREps.php b/vendor/chillerlan/php-qrcode/src/Output/QREps.php
new file mode 100644
index 000000000..76dd50b37
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QREps.php
@@ -0,0 +1,173 @@
+<?php
+/**
+ * Class QREps
+ *
+ * @created 09.05.2022
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2022 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function array_values, count, date, implode, is_array, is_numeric, max, min, round, sprintf;
+
+/**
+ * Encapsulated Postscript (EPS) output
+ *
+ * @see https://github.com/t0k4rt/phpqrcode/blob/bb29e6eb77e0a2a85bb0eb62725e0adc11ff5a90/qrvect.php#L52-L137
+ * @see https://web.archive.org/web/20170818010030/http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/postscript/pdfs/5002.EPSF_Spec.pdf
+ * @see https://web.archive.org/web/20210419003859/https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf
+ * @see https://github.com/chillerlan/php-qrcode/discussions/148
+ */
+class QREps extends QROutputAbstract{
+
+ public const MIME_TYPE = 'application/postscript';
+
+ /**
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_array($value) || count($value) < 3){
+ return false;
+ }
+
+ // check the first values of the array
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 3){
+ break;
+ }
+
+ if(!is_numeric($val)){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $value
+ *
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):string{
+ $values = [];
+
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 3){
+ break;
+ }
+
+ // clamp value and convert from int 0-255 to float 0-1 RGB/CMYK range
+ $values[] = round((max(0, min(255, intval($val))) / 255), 6);
+ }
+
+ return $this->formatColor($values);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return $this->formatColor(($isDark) ? [0.0, 0.0, 0.0] : [1.0, 1.0, 1.0]);
+ }
+
+ /**
+ * Set the color format string
+ *
+ * 4 values in the color array will be interpreted as CMYK, 3 as RGB
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function formatColor(array $values):string{
+ $count = count($values);
+
+ if($count < 3){
+ throw new QRCodeOutputException('invalid color value');
+ }
+
+ $format = ($count === 4)
+ // CMYK
+ ? '%f %f %f %f C'
+ // RGB
+ : '%f %f %f R';
+
+ return sprintf($format, ...$values);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function dump(?string $file = null):string{
+ [$width, $height] = $this->getOutputDimensions();
+
+ $eps = [
+ // main header
+ '%!PS-Adobe-3.0 EPSF-3.0',
+ '%%Creator: php-qrcode (https://github.com/chillerlan/php-qrcode)',
+ '%%Title: QR Code',
+ sprintf('%%%%CreationDate: %1$s', date('c')),
+ '%%DocumentData: Clean7Bit',
+ '%%LanguageLevel: 3',
+ sprintf('%%%%BoundingBox: 0 0 %s %s', $width, $height),
+ '%%EndComments',
+ // function definitions
+ '%%BeginProlog',
+ '/F { rectfill } def',
+ '/R { setrgbcolor } def',
+ '/C { setcmykcolor } def',
+ '%%EndProlog',
+ ];
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $eps[] = $this->prepareModuleValue($this->options->bgColor);
+ $eps[] = sprintf('0 0 %s %s F', $width, $height);
+ }
+
+ // create the path elements
+ $paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => $this->module($x, $y, $M_TYPE));
+
+ foreach($paths as $M_TYPE => $path){
+
+ if(empty($path)){
+ continue;
+ }
+
+ $eps[] = $this->getModuleValue($M_TYPE);
+ $eps[] = implode("\n", $path);
+ }
+
+ // end file
+ $eps[] = '%%EOF';
+
+ $data = implode("\n", $eps);
+
+ $this->saveToFile($data, $file);
+
+ return $data;
+ }
+
+ /**
+ * Returns a path segment for a single module
+ */
+ protected function module(int $x, int $y, int $M_TYPE):string{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return '';
+ }
+
+ $outputX = ($x * $this->scale);
+ // Actual size - one block = Topmost y pos.
+ $top = ($this->length - $this->scale);
+ // Apparently y-axis is inverted (y0 is at bottom and not top) in EPS, so we have to switch the y-axis here
+ $outputY = ($top - ($y * $this->scale));
+
+ return sprintf('%d %d %d %d F', $outputX, $outputY, $this->scale, $this->scale);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php
index a15ae9ff3..8f2482cba 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRFpdf.php
@@ -2,24 +2,20 @@
/**
* 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
+ *
+ * @see https://github.com/chillerlan/php-qrcode/pull/49
*/
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;
+use function array_values, class_exists, count, intval, is_array, is_numeric, max, min;
/**
* QRFpdf output module (requires fpdf)
@@ -29,12 +25,23 @@ use function array_values, class_exists, count, is_array;
*/
class QRFpdf extends QROutputAbstract{
+ public const MIME_TYPE = 'application/pdf';
+
+ protected FPDF $fpdf;
+ protected ?array $prevColor = null;
+
+ /**
+ * QRFpdf constructor.
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
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.'
+ throw new QRCodeOutputException(
+ 'The QRFpdf output requires FPDF (https://github.com/Setasign/FPDF)'.
+ ' as dependency but the class "\\FPDF" couldn\'t be found.'
);
// @codeCoverageIgnoreEnd
}
@@ -45,69 +52,128 @@ class QRFpdf extends QROutputAbstract{
/**
* @inheritDoc
*/
- protected function setModuleValues():void{
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_array($value) || count($value) < 3){
+ return false;
+ }
- foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
- $v = $this->options->moduleValues[$M_TYPE] ?? null;
+ // check the first 3 values of the array
+ foreach(array_values($value) as $i => $val){
- if(!is_array($v) || count($v) < 3){
- $this->moduleValues[$M_TYPE] = $defaultValue
- ? [0, 0, 0]
- : [255, 255, 255];
+ if($i > 2){
+ break;
}
- else{
- $this->moduleValues[$M_TYPE] = array_values($v);
+
+ if(!is_numeric($val)){
+ return false;
}
}
+ return true;
}
/**
- * @inheritDoc
+ * @param array $value
*
- * @return string|\FPDF
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function prepareModuleValue($value):array{
+ $values = [];
+
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 2){
+ break;
+ }
+
+ $values[] = max(0, min(255, intval($val)));
+ }
+
+ if(count($values) !== 3){
+ throw new QRCodeOutputException('invalid color value');
+ }
+
+ return $values;
+ }
+
+ /**
+ * @inheritDoc
*/
- public function dump(string $file = null){
- $file ??= $this->options->cachefile;
+ protected function getDefaultModuleValue(bool $isDark):array{
+ return ($isDark) ? [0, 0, 0] : [255, 255, 255];
+ }
- $fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, [$this->length, $this->length]);
+ /**
+ * Initializes an FPDF instance
+ */
+ protected function initFPDF():FPDF{
+ $fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
$fpdf->AddPage();
- $prevColor = null;
+ return $fpdf;
+ }
- foreach($this->matrix->matrix() as $y => $row){
+ /**
+ * @inheritDoc
+ *
+ * @return string|\FPDF
+ */
+ public function dump(?string $file = null, ?FPDF $fpdf = null){
+ $this->fpdf = ($fpdf ?? $this->initFPDF());
- foreach($row as $x => $M_TYPE){
- /** @var int $M_TYPE */
- $color = $this->moduleValues[$M_TYPE];
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $bgColor = $this->prepareModuleValue($this->options->bgColor);
+ [$width, $height] = $this->getOutputDimensions();
- if($prevColor === null || $prevColor !== $color){
- /** @phan-suppress-next-line PhanParamTooFewUnpack */
- $fpdf->SetFillColor(...$color);
- $prevColor = $color;
- }
+ /** @phan-suppress-next-line PhanParamTooFewUnpack */
+ $this->fpdf->SetFillColor(...$bgColor);
+ $this->fpdf->Rect(0, 0, $width, $height, 'F');
+ }
- $fpdf->Rect($x * $this->scale, $y * $this->scale, 1 * $this->scale, 1 * $this->scale, 'F');
- }
+ $this->prevColor = null;
+ foreach($this->matrix->getMatrix() as $y => $row){
+ foreach($row as $x => $M_TYPE){
+ $this->module($x, $y, $M_TYPE);
+ }
}
if($this->options->returnResource){
- return $fpdf;
+ return $this->fpdf;
}
- $pdfData = $fpdf->Output('S');
+ $pdfData = $this->fpdf->Output('S');
- if($file !== null){
- $this->saveToFile($pdfData, $file);
- }
+ $this->saveToFile($pdfData, $file);
- if($this->options->imageBase64){
- $pdfData = sprintf('data:application/pdf;base64,%s', base64_encode($pdfData));
+ if($this->options->outputBase64){
+ $pdfData = $this->toBase64DataURI($pdfData);
}
return $pdfData;
}
+ /**
+ * Renders a single module
+ */
+ protected function module(int $x, int $y, int $M_TYPE):void{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return;
+ }
+
+ $color = $this->getModuleValue($M_TYPE);
+
+ if($color !== null && $color !== $this->prevColor){
+ /** @phan-suppress-next-line PhanParamTooFewUnpack */
+ $this->fpdf->SetFillColor(...$color);
+ $this->prevColor = $color;
+ }
+
+ $this->fpdf->Rect(($x * $this->scale), ($y * $this->scale), $this->scale, $this->scale, 'F');
+ }
+
}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php
new file mode 100644
index 000000000..25db1c902
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImage.php
@@ -0,0 +1,400 @@
+<?php
+/**
+ * Class QRGdImage
+ *
+ * @created 05.12.2015
+ * @author Smiley <smiley@chillerlan.net>
+ * @copyright 2015 Smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\Settings\SettingsContainerInterface;
+use ErrorException;
+use Throwable;
+use function array_values, count, extension_loaded, imagebmp, imagecolorallocate, imagecolortransparent,
+ imagecreatetruecolor, imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng,
+ imagescale, imagetypes, imagewebp, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start,
+ restore_error_handler, set_error_handler, sprintf;
+use const IMG_BMP, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP;
+
+/**
+ * Converts the matrix into GD images, raw or base64 output (requires ext-gd)
+ *
+ * @see https://php.net/manual/book.image.php
+ *
+ * @deprecated 5.0.0 this class will be made abstract in future versions,
+ * calling it directly is deprecated - use one of the child classes instead
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+class QRGdImage extends QROutputAbstract{
+
+ /**
+ * The GD image resource
+ *
+ * @see imagecreatetruecolor()
+ * @var resource|\GdImage
+ *
+ * @todo: add \GdImage type in v6
+ */
+ protected $image;
+
+ /**
+ * The allocated background color
+ *
+ * @see \imagecolorallocate()
+ */
+ protected int $background;
+
+ /**
+ * Whether we're running in upscale mode (scale < 20)
+ *
+ * @see \chillerlan\QRCode\QROptions::$drawCircularModules
+ */
+ protected bool $upscaled = false;
+
+ /**
+ * @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ * @noinspection PhpMissingParentConstructorInspection
+ */
+ public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
+ $this->options = $options;
+ $this->matrix = $matrix;
+
+ $this->checkGD();
+
+ if($this->options->invertMatrix){
+ $this->matrix->invert();
+ }
+
+ $this->copyVars();
+ $this->setMatrixDimensions();
+ }
+
+ /**
+ * Checks whether GD is installed and if the given mode is supported
+ *
+ * @return void
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ * @codeCoverageIgnore
+ */
+ protected function checkGD():void{
+
+ if(!extension_loaded('gd')){
+ throw new QRCodeOutputException('ext-gd not loaded');
+ }
+
+ $modes = [
+ self::GDIMAGE_BMP => IMG_BMP,
+ self::GDIMAGE_GIF => IMG_GIF,
+ self::GDIMAGE_JPG => IMG_JPG,
+ self::GDIMAGE_PNG => IMG_PNG,
+ self::GDIMAGE_WEBP => IMG_WEBP,
+ ];
+
+ // likely using default or custom output
+ if(!isset($modes[$this->options->outputType])){
+ return;
+ }
+
+ $mode = $modes[$this->options->outputType];
+
+ if((imagetypes() & $mode) !== $mode){
+ throw new QRCodeOutputException(sprintf('output mode "%s" not supported', $this->options->outputType));
+ }
+
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_array($value) || count($value) < 3){
+ return false;
+ }
+
+ // check the first 3 values of the array
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 2){
+ break;
+ }
+
+ if(!is_numeric($val)){
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $value
+ *
+ * @inheritDoc
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function prepareModuleValue($value):int{
+ $values = [];
+
+ foreach(array_values($value) as $i => $val){
+
+ if($i > 2){
+ break;
+ }
+
+ $values[] = max(0, min(255, intval($val)));
+ }
+
+ /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
+ $color = imagecolorallocate($this->image, ...$values);
+
+ if($color === false){
+ throw new QRCodeOutputException('could not set color: imagecolorallocate() error');
+ }
+
+ return $color;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):int{
+ return $this->prepareModuleValue(($isDark) ? [0, 0, 0] : [255, 255, 255]);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @return string|resource|\GdImage
+ *
+ * @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
+ * @throws \ErrorException
+ */
+ public function dump(?string $file = null){
+
+ set_error_handler(function(int $errno, string $errstr):bool{
+ throw new ErrorException($errstr, $errno);
+ });
+
+ $this->image = $this->createImage();
+ // set module values after image creation because we need the GdImage instance
+ $this->setModuleValues();
+ $this->setBgColor();
+
+ imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $this->background);
+
+ $this->drawImage();
+
+ if($this->upscaled){
+ // scale down to the expected size
+ $this->image = imagescale($this->image, ($this->length / 10), ($this->length / 10));
+ $this->upscaled = false;
+ }
+
+ // set transparency after scaling, otherwise it would be undone
+ // @see https://www.php.net/manual/en/function.imagecolortransparent.php#77099
+ $this->setTransparencyColor();
+
+ if($this->options->returnResource){
+ restore_error_handler();
+
+ return $this->image;
+ }
+
+ $imageData = $this->dumpImage();
+
+ $this->saveToFile($imageData, $file);
+
+ if($this->options->outputBase64){
+ // @todo: remove mime parameter in v6
+ $imageData = $this->toBase64DataURI($imageData, 'image/'.$this->options->outputType);
+ }
+
+ restore_error_handler();
+
+ return $imageData;
+ }
+
+ /**
+ * Creates a new GdImage resource and scales it if necessary
+ *
+ * we're scaling the image up in order to draw crisp round circles, otherwise they appear square-y on small scales
+ *
+ * @see https://github.com/chillerlan/php-qrcode/issues/23
+ *
+ * @return \GdImage|resource
+ */
+ protected function createImage(){
+
+ if($this->drawCircularModules && $this->options->gdImageUseUpscale && $this->options->scale < 20){
+ // increase the initial image size by 10
+ $this->length *= 10;
+ $this->scale *= 10;
+ $this->upscaled = true;
+ }
+
+ return imagecreatetruecolor($this->length, $this->length);
+ }
+
+ /**
+ * Sets the background color
+ */
+ protected function setBgColor():void{
+
+ if(isset($this->background)){
+ return;
+ }
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $this->background = $this->prepareModuleValue($this->options->bgColor);
+
+ return;
+ }
+
+ $this->background = $this->prepareModuleValue([255, 255, 255]);
+ }
+
+ /**
+ * Sets the transparency color
+ */
+ protected function setTransparencyColor():void{
+
+ // @todo: the jpg skip can be removed in v6
+ if($this->options->outputType === QROutputInterface::GDIMAGE_JPG || !$this->options->imageTransparent){
+ return;
+ }
+
+ $transparencyColor = $this->background;
+
+ if($this::moduleValueIsValid($this->options->transparencyColor)){
+ $transparencyColor = $this->prepareModuleValue($this->options->transparencyColor);
+ }
+
+ imagecolortransparent($this->image, $transparencyColor);
+ }
+
+ /**
+ * Draws the QR image
+ */
+ protected function drawImage():void{
+ foreach($this->matrix->getMatrix() as $y => $row){
+ foreach($row as $x => $M_TYPE){
+ $this->module($x, $y, $M_TYPE);
+ }
+ }
+ }
+
+ /**
+ * Creates a single QR pixel with the given settings
+ */
+ protected function module(int $x, int $y, int $M_TYPE):void{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return;
+ }
+
+ $color = $this->getModuleValue($M_TYPE);
+
+ if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
+ imagefilledellipse(
+ $this->image,
+ (($x * $this->scale) + intdiv($this->scale, 2)),
+ (($y * $this->scale) + intdiv($this->scale, 2)),
+ (int)($this->circleDiameter * $this->scale),
+ (int)($this->circleDiameter * $this->scale),
+ $color
+ );
+
+ return;
+ }
+
+ imagefilledrectangle(
+ $this->image,
+ ($x * $this->scale),
+ ($y * $this->scale),
+ (($x + 1) * $this->scale),
+ (($y + 1) * $this->scale),
+ $color
+ );
+ }
+
+ /**
+ * Renders the image with the gdimage function for the desired output
+ *
+ * @see \imagebmp()
+ * @see \imagegif()
+ * @see \imagejpeg()
+ * @see \imagepng()
+ * @see \imagewebp()
+ *
+ * @todo: v6.0: make abstract and call from child classes
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ * @codeCoverageIgnore
+ */
+ protected function renderImage():void{
+
+ switch($this->options->outputType){
+ case QROutputInterface::GDIMAGE_BMP:
+ imagebmp($this->image, null, ($this->options->quality > 0));
+ break;
+ case QROutputInterface::GDIMAGE_GIF:
+ imagegif($this->image);
+ break;
+ case QROutputInterface::GDIMAGE_JPG:
+ imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
+ break;
+ case QROutputInterface::GDIMAGE_WEBP:
+ imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
+ break;
+ // silently default to png output
+ case QROutputInterface::GDIMAGE_PNG:
+ default:
+ imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
+ }
+
+ }
+
+ /**
+ * Creates the final image by calling the desired GD output function
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
+ */
+ protected function dumpImage():string{
+ $exception = null;
+ $imageData = null;
+
+ ob_start();
+
+ try{
+ $this->renderImage();
+
+ $imageData = ob_get_contents();
+ imagedestroy($this->image);
+ }
+ // not going to cover edge cases
+ // @codeCoverageIgnoreStart
+ catch(Throwable $e){
+ $exception = $e;
+ }
+ // @codeCoverageIgnoreEnd
+
+ ob_end_clean();
+
+ // throw here in case an exception happened within the output buffer
+ if($exception instanceof Throwable){
+ throw new QRCodeOutputException($exception->getMessage());
+ }
+
+ return $imageData;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php
new file mode 100644
index 000000000..268ebe7c2
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageBMP.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Class QRGdImageBMP
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagebmp;
+
+/**
+ * GdImage bmp output
+ *
+ * @see \imagebmp()
+ */
+class QRGdImageBMP extends QRGdImage{
+
+ public const MIME_TYPE = 'image/bmp';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagebmp($this->image, null, ($this->options->quality > 0));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php
new file mode 100644
index 000000000..a02130907
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageGIF.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Class QRGdImageGIF
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagegif;
+
+/**
+ * GdImage gif output
+ *
+ * @see \imagegif()
+ */
+class QRGdImageGIF extends QRGdImage{
+
+ public const MIME_TYPE = 'image/gif';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagegif($this->image);
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php
new file mode 100644
index 000000000..6be36e2fe
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageJPEG.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Class QRGdImageJPEG
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagejpeg, max, min;
+
+/**
+ * GdImage jpeg output
+ *
+ * @see \imagejpeg()
+ */
+class QRGdImageJPEG extends QRGdImage{
+
+ public const MIME_TYPE = 'image/jpg';
+
+ /**
+ * @inheritDoc
+ */
+ protected function setTransparencyColor():void{
+ // noop - transparency is not supported
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php
new file mode 100644
index 000000000..2db3fd5b4
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImagePNG.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Class QRGdImagePNG
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagepng, max, min;
+
+/**
+ * GdImage png output
+ *
+ * @see \imagepng()
+ */
+class QRGdImagePNG extends QRGdImage{
+
+ public const MIME_TYPE = 'image/png';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php
new file mode 100644
index 000000000..cf8dfa9a5
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRGdImageWEBP.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Class QRGdImageWEBP
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function imagewebp, max, min;
+
+/**
+ * GdImage webp output
+ *
+ * @see \imagewebp()
+ */
+class QRGdImageWEBP extends QRGdImage{
+
+ public const MIME_TYPE = 'image/webp';
+
+ /**
+ * @inheritDoc
+ */
+ protected function renderImage():void{
+ imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRImage.php b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php
index 8f533d341..cda496d36 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QRImage.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRImage.php
@@ -2,216 +2,18 @@
/**
* Class QRImage
*
- * @filesource QRImage.php
- * @created 05.12.2015
- * @package chillerlan\QRCode\Output
- * @author Smiley <smiley@chillerlan.net>
- * @copyright 2015 Smiley
+ * @created 14.12.2021
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2021 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
+ * @deprecated 5.0.0 backward compatibility, use QRGdImage instead
+ * @see \chillerlan\QRCode\Output\QRGdImage
*/
-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
- );
- }
+class QRImage extends QRGdImage{
}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php
index 49516d30e..214311a93 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRImagick.php
@@ -2,9 +2,7 @@
/**
* Class QRImagick
*
- * @filesource QRImagick.php
* @created 04.07.2018
- * @package chillerlan\QRCode\Output
* @author smiley <smiley@chillerlan.net>
* @copyright 2018 smiley
* @license MIT
@@ -15,51 +13,98 @@
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;
+use finfo, Imagick, ImagickDraw, ImagickPixel;
+use function extension_loaded, in_array, is_string, max, min, preg_match, strlen;
+use const FILEINFO_MIME_TYPE;
/**
* ImageMagick output module (requires ext-imagick)
*
- * @see http://php.net/manual/book.imagick.php
- * @see http://phpimagick.com
+ * @see https://php.net/manual/book.imagick.php
+ * @see https://phpimagick.com
*/
class QRImagick extends QROutputAbstract{
+ /**
+ * The main image instance
+ */
protected Imagick $imagick;
/**
+ * The main draw instance
+ */
+ protected ImagickDraw $imagickDraw;
+
+ /**
+ * The allocated background color
+ */
+ protected ImagickPixel $backgroundColor;
+
+ /**
* @inheritDoc
+ *
+ * @throws \chillerlan\QRCode\Output\QRCodeOutputException
*/
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
- if(!extension_loaded('imagick')){
- throw new QRCodeException('ext-imagick not loaded'); // @codeCoverageIgnore
+ foreach(['fileinfo', 'imagick'] as $ext){
+ if(!extension_loaded($ext)){
+ throw new QRCodeOutputException(sprintf('ext-%s not loaded', $ext)); // @codeCoverageIgnore
+ }
}
parent::__construct($options, $matrix);
}
/**
+ * note: we're not necessarily validating the several values, just checking the general syntax
+ *
+ * @see https://www.php.net/manual/imagickpixel.construct.php
* @inheritDoc
*/
- protected function setModuleValues():void{
+ public static function moduleValueIsValid($value):bool{
- foreach($this::DEFAULT_MODULE_VALUES as $type => $defaultValue){
- $v = $this->options->moduleValues[$type] ?? null;
+ if(!is_string($value)){
+ return false;
+ }
- 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);
- }
+ $value = trim($value);
+
+ // hex notation
+ // #rgb(a)
+ // #rrggbb(aa)
+ // #rrrrggggbbbb(aaaa)
+ // ...
+ if(preg_match('/^#[a-f\d]+$/i', $value) && in_array((strlen($value) - 1), [3, 4, 6, 8, 9, 12, 16, 24, 32], true)){
+ return true;
+ }
+
+ // css (-like) func(...values)
+ if(preg_match('#^(graya?|hs(b|la?)|rgba?)\([\d .,%]+\)$#i', $value)){
+ return true;
}
+
+ // predefined css color
+ if(preg_match('/^[a-z]+$/i', $value)){
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):ImagickPixel{
+ return new ImagickPixel($value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):ImagickPixel{
+ return $this->prepareModuleValue(($isDark) ? '#000' : '#fff');
}
/**
@@ -67,18 +112,14 @@ class QRImagick extends QROutputAbstract{
*
* @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
- );
+ public function dump(?string $file = null){
+ $this->setBgColor();
+
+ $this->imagick = $this->createImage();
$this->drawImage();
+ // set transparency color after all operations
+ $this->setTransparencyColor();
if($this->options->returnResource){
return $this->imagick;
@@ -86,34 +127,109 @@ class QRImagick extends QROutputAbstract{
$imageData = $this->imagick->getImageBlob();
- if($file !== null){
- $this->saveToFile($imageData, $file);
+ $this->imagick->destroy();
+
+ $this->saveToFile($imageData, $file);
+
+ if($this->options->outputBase64){
+ $imageData = $this->toBase64DataURI($imageData, (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData));
}
return $imageData;
}
/**
+ * Sets the background color
+ */
+ protected function setBgColor():void{
+
+ if($this::moduleValueIsValid($this->options->bgColor)){
+ $this->backgroundColor = $this->prepareModuleValue($this->options->bgColor);
+
+ return;
+ }
+
+ $this->backgroundColor = $this->prepareModuleValue('white');
+ }
+
+ /**
+ * Creates a new Imagick instance
+ */
+ protected function createImage():Imagick{
+ $imagick = new Imagick;
+ [$width, $height] = $this->getOutputDimensions();
+
+ $imagick->newImage($width, $height, $this->backgroundColor, $this->options->imagickFormat);
+
+ if($this->options->quality > -1){
+ $imagick->setImageCompressionQuality(max(0, min(100, $this->options->quality)));
+ }
+
+ return $imagick;
+ }
+
+ /**
+ * Sets the transparency color
+ */
+ protected function setTransparencyColor():void{
+
+ if(!$this->options->imageTransparent){
+ return;
+ }
+
+ $transparencyColor = $this->backgroundColor;
+
+ if($this::moduleValueIsValid($this->options->transparencyColor)){
+ $transparencyColor = $this->prepareModuleValue($this->options->transparencyColor);
+ }
+
+ $this->imagick->transparentPaintImage($transparencyColor, 0.0, 10, false);
+ }
+
+ /**
* Creates the QR image via ImagickDraw
*/
protected function drawImage():void{
- $draw = new ImagickDraw;
+ $this->imagickDraw = new ImagickDraw;
+ $this->imagickDraw->setStrokeWidth(0);
- foreach($this->matrix->matrix() as $y => $row){
+ foreach($this->matrix->getMatrix() 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->module($x, $y, $M_TYPE);
}
}
- $this->imagick->drawImage($draw);
+ $this->imagick->drawImage($this->imagickDraw);
+ }
+
+ /**
+ * draws a single pixel at the given position
+ */
+ protected function module(int $x, int $y, int $M_TYPE):void{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return;
+ }
+
+ $this->imagickDraw->setFillColor($this->getModuleValue($M_TYPE));
+
+ if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
+ $this->imagickDraw->circle(
+ (($x + 0.5) * $this->scale),
+ (($y + 0.5) * $this->scale),
+ (($x + 0.5 + $this->circleRadius) * $this->scale),
+ (($y + 0.5) * $this->scale)
+ );
+
+ return;
+ }
+
+ $this->imagickDraw->rectangle(
+ ($x * $this->scale),
+ ($y * $this->scale),
+ ((($x + 1) * $this->scale) - 1),
+ ((($y + 1) * $this->scale) - 1)
+ );
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php
index 06d6e88cb..240bd45ad 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkup.php
@@ -2,9 +2,7 @@
/**
* Class QRMarkup
*
- * @filesource QRMarkup.php
* @created 17.12.2016
- * @package chillerlan\QRCode\Output
* @author Smiley <smiley@chillerlan.net>
* @copyright 2016 Smiley
* @license MIT
@@ -12,149 +10,85 @@
namespace chillerlan\QRCode\Output;
-use chillerlan\QRCode\QRCode;
-
-use function is_string, sprintf, strip_tags, trim;
+use function is_string, preg_match, strip_tags, trim;
/**
- * Converts the matrix into markup types: HTML, SVG, ...
+ * Abstract for markup types: HTML, SVG, ... XML anyone?
*/
-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">';
+abstract class QRMarkup extends QROutputAbstract{
/**
+ * note: we're not necessarily validating the several values, just checking the general syntax
+ * note: css4 colors are not included
+ *
+ * @todo: XSS proof
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
* @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), '\'"');
- }
+ public static function moduleValueIsValid($value):bool{
+ if(!is_string($value)){
+ return false;
}
- }
-
- /**
- * HTML output
- */
- protected function html(string $file = null):string{
+ $value = trim(strip_tags($value), " '\"\r\n\t");
- $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;
+ // hex notation
+ // #rgb(a)
+ // #rrggbb(aa)
+ if(preg_match('/^#([\da-f]{3}){1,2}$|^#([\da-f]{4}){1,2}$/i', $value)){
+ return true;
}
- $html .= '</div>'.$this->options->eol;
+ // css: hsla/rgba(...values)
+ if(preg_match('#^(hsla?|rgba?)\([\d .,%/]+\)$#i', $value)){
+ return true;
+ }
- if($file !== null){
- return '<!DOCTYPE html>'.
- '<head><meta charset="UTF-8"><title>QR Code</title></head>'.
- '<body>'.$this->options->eol.$html.'</body>';
+ // predefined css color
+ if(preg_match('/^[a-z]+$/i', $value)){
+ return true;
}
- return $html;
+ return false;
}
/**
- * SVG output
- *
- * @see https://github.com/codemasher/php-qrcode/pull/5
+ * @inheritDoc
*/
- 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
- );
- }
+ protected function prepareModuleValue($value):string{
+ return trim(strip_tags($value), " '\"\r\n\t");
+ }
- }
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return ($isDark) ? '#000' : '#fff';
+ }
- // close svg
- $svg .= '</svg>'.$this->options->eol;
+ /**
+ * @inheritDoc
+ */
+ public function dump(?string $file = null):string{
+ $data = $this->createMarkup($file !== null);
- // 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;
- }
+ $this->saveToFile($data, $file);
- if($this->options->imageBase64){
- $svg = sprintf('data:image/svg+xml;base64,%s', base64_encode($svg));
- }
+ return $data;
+ }
- return $svg;
+ /**
+ * returns a string with all css classes for the current element
+ */
+ protected function getCssClass(int $M_TYPE = 0):string{
+ return $this->options->cssClass;
}
+ /**
+ * returns the fully parsed and rendered markup string for the given input
+ */
+ abstract protected function createMarkup(bool $saveToFile):string;
+
}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php b/vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php
new file mode 100644
index 000000000..65dc49a8a
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkupHTML.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Class QRMarkupHTML
+ *
+ * @created 06.06.2022
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2022 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function implode, sprintf;
+
+/**
+ * HTML output (a cheap markup substitute when SVG is not available or not an option)
+ */
+class QRMarkupHTML extends QRMarkup{
+
+ public const MIME_TYPE = 'text/html';
+
+ /**
+ * @inheritDoc
+ */
+ protected function createMarkup(bool $saveToFile):string{
+ $rows = [];
+ $cssClass = $this->getCssClass();
+
+ foreach($this->matrix->getMatrix() as $row){
+ $element = '<span style="background: %s;"></span>';
+ $modules = array_map(fn(int $M_TYPE):string => sprintf($element, $this->getModuleValue($M_TYPE)), $row);
+
+ $rows[] = sprintf('<div>%s</div>%s', implode('', $modules), $this->eol);
+ }
+
+ $html = sprintf('<div class="%1$s">%3$s%2$s</div>%3$s', $cssClass, implode('', $rows), $this->eol);
+
+ // wrap the snippet into a body when saving to file
+ if($saveToFile){
+ $html = sprintf(
+ '<!DOCTYPE html><html lang="none">%2$s<head>%2$s<meta charset="UTF-8">%2$s'.
+ '<title>QR Code</title></head>%2$s<body>%1$s</body>%2$s</html>',
+ $html,
+ $this->eol
+ );
+ }
+
+ return $html;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRMarkupSVG.php b/vendor/chillerlan/php-qrcode/src/Output/QRMarkupSVG.php
new file mode 100644
index 000000000..735c4180b
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRMarkupSVG.php
@@ -0,0 +1,200 @@
+<?php
+/**
+ * Class QRMarkupSVG
+ *
+ * @created 06.06.2022
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2022 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function array_chunk, implode, is_string, preg_match, sprintf, trim;
+
+/**
+ * SVG output
+ *
+ * @see https://github.com/codemasher/php-qrcode/pull/5
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG
+ * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
+ * @see https://lea.verou.me/blog/2019/05/utility-convert-svg-path-to-all-relative-or-all-absolute-commands/
+ * @see https://codepen.io/leaverou/full/RmwzKv
+ * @see https://jakearchibald.github.io/svgomg/
+ * @see https://web.archive.org/web/20200220211445/http://apex.infogridpacific.com/SVG/svg-tutorial-contents.html
+ */
+class QRMarkupSVG extends QRMarkup{
+
+ public const MIME_TYPE = 'image/svg+xml';
+
+ /**
+ * @todo: XSS proof
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+
+ if(!is_string($value)){
+ return false;
+ }
+
+ $value = trim($value);
+
+ // url(...)
+ if(preg_match('~^url\([-/#a-z\d]+\)$~i', $value)){
+ return true;
+ }
+
+ // otherwise check for standard css notation
+ return parent::moduleValueIsValid($value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getOutputDimensions():array{
+ return [$this->moduleCount, $this->moduleCount];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getCssClass(int $M_TYPE = 0):string{
+ return implode(' ', [
+ 'qr-'.($this::LAYERNAMES[$M_TYPE] ?? $M_TYPE),
+ $this->matrix->isDark($M_TYPE) ? 'dark' : 'light',
+ $this->options->cssClass,
+ ]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function createMarkup(bool $saveToFile):string{
+ $svg = $this->header();
+
+ if(!empty($this->options->svgDefs)){
+ $svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->eol);
+ }
+
+ $svg .= $this->paths();
+
+ // close svg
+ $svg .= sprintf('%1$s</svg>%1$s', $this->eol);
+
+ // transform to data URI only when not saving to file
+ if(!$saveToFile && $this->options->outputBase64){
+ $svg = $this->toBase64DataURI($svg);
+ }
+
+ return $svg;
+ }
+
+ /**
+ * returns the value for the SVG viewBox attribute
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox
+ * @see https://css-tricks.com/scale-svg/#article-header-id-3
+ */
+ protected function getViewBox():string{
+ [$width, $height] = $this->getOutputDimensions();
+
+ return sprintf('0 0 %s %s', $width, $height);
+ }
+
+ /**
+ * returns the <svg> header with the given options parsed
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
+ */
+ protected function header():string{
+
+ $header = sprintf(
+ '<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" viewBox="%2$s" preserveAspectRatio="%3$s">%4$s',
+ $this->options->cssClass,
+ $this->getViewBox(),
+ $this->options->svgPreserveAspectRatio,
+ $this->eol
+ );
+
+ if($this->options->svgAddXmlHeader){
+ $header = sprintf('<?xml version="1.0" encoding="UTF-8"?>%s%s', $this->eol, $header);
+ }
+
+ return $header;
+ }
+
+ /**
+ * returns one or more SVG <path> elements
+ */
+ protected function paths():string{
+ $paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => $this->module($x, $y, $M_TYPE));
+ $svg = [];
+
+ // create the path elements
+ foreach($paths as $M_TYPE => $modules){
+ // limit the total line length
+ $chunks = array_chunk($modules, 100);
+ $chonks = [];
+
+ foreach($chunks as $chunk){
+ $chonks[] = implode(' ', $chunk);
+ }
+
+ $path = implode($this->eol, $chonks);
+
+ if(empty($path)){
+ continue;
+ }
+
+ $svg[] = $this->path($path, $M_TYPE);
+ }
+
+ return implode($this->eol, $svg);
+ }
+
+ /**
+ * renders and returns a single <path> element
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
+ */
+ protected function path(string $path, int $M_TYPE):string{
+
+ if($this->options->svgUseFillAttributes){
+ return sprintf(
+ '<path class="%s" fill="%s" d="%s"/>',
+ $this->getCssClass($M_TYPE),
+ $this->getModuleValue($M_TYPE),
+ $path
+ );
+ }
+
+ return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
+ }
+
+ /**
+ * returns a path segment for a single module
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
+ */
+ protected function module(int $x, int $y, int $M_TYPE):string{
+
+ if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
+ return '';
+ }
+
+ if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
+ // string interpolation: ugly and fast
+ $ix = ($x + 0.5 - $this->circleRadius);
+ $iy = ($y + 0.5);
+
+ // phpcs:ignore
+ return "M$ix $iy a$this->circleRadius $this->circleRadius 0 1 0 $this->circleDiameter 0 a$this->circleRadius $this->circleRadius 0 1 0 -$this->circleDiameter 0Z";
+ }
+
+ // phpcs:ignore
+ return "M$x $y h1 v1 h-1Z";
+ }
+
+}
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;
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php b/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php
index b07b8e7a5..7d2315180 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QROutputInterface.php
@@ -2,9 +2,7 @@
/**
* Interface QROutputInterface,
*
- * @filesource QROutputInterface.php
* @created 02.12.2015
- * @package chillerlan\QRCode\Output
* @author Smiley <smiley@chillerlan.net>
* @copyright 2015 Smiley
* @license MIT
@@ -19,36 +17,210 @@ use chillerlan\QRCode\Data\QRMatrix;
*/
interface QROutputInterface{
- const DEFAULT_MODULE_VALUES = [
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const MARKUP_HTML = 'html';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const MARKUP_SVG = 'svg';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const GDIMAGE_BMP = 'bmp';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const GDIMAGE_GIF = 'gif';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const GDIMAGE_JPG = 'jpg';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const GDIMAGE_PNG = 'png';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const GDIMAGE_WEBP = 'webp';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const STRING_JSON = 'json';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const STRING_TEXT = 'text';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const IMAGICK = 'imagick';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const FPDF = 'fpdf';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const EPS = 'eps';
+
+ /**
+ * @var string
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const CUSTOM = 'custom';
+
+ /**
+ * Map of built-in output modes => class FQN
+ *
+ * @var string[]
+ * @deprecated 5.0.0 <no replacement>
+ * @see https://github.com/chillerlan/php-qrcode/issues/223
+ */
+ public const MODES = [
+ self::MARKUP_SVG => QRMarkupSVG::class,
+ self::MARKUP_HTML => QRMarkupHTML::class,
+ self::GDIMAGE_BMP => QRGdImageBMP::class,
+ self::GDIMAGE_GIF => QRGdImageGIF::class,
+ self::GDIMAGE_JPG => QRGdImageJPEG::class,
+ self::GDIMAGE_PNG => QRGdImagePNG::class,
+ self::GDIMAGE_WEBP => QRGdImageWEBP::class,
+ self::STRING_JSON => QRStringJSON::class,
+ self::STRING_TEXT => QRStringText::class,
+ self::IMAGICK => QRImagick::class,
+ self::FPDF => QRFpdf::class,
+ self::EPS => QREps::class,
+ ];
+
+ /**
+ * Map of module type => default value
+ *
+ * @var bool[]
+ */
+ public 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
+ QRMatrix::M_NULL => false,
+ QRMatrix::M_DARKMODULE_LIGHT => false,
+ QRMatrix::M_DATA => false,
+ QRMatrix::M_FINDER => false,
+ QRMatrix::M_SEPARATOR => false,
+ QRMatrix::M_ALIGNMENT => false,
+ QRMatrix::M_TIMING => false,
+ QRMatrix::M_FORMAT => false,
+ QRMatrix::M_VERSION => false,
+ QRMatrix::M_QUIETZONE => false,
+ QRMatrix::M_LOGO => false,
+ QRMatrix::M_FINDER_DOT_LIGHT => false,
// 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
+ QRMatrix::M_DARKMODULE => true,
+ QRMatrix::M_DATA_DARK => true,
+ QRMatrix::M_FINDER_DARK => true,
+ QRMatrix::M_SEPARATOR_DARK => true,
+ QRMatrix::M_ALIGNMENT_DARK => true,
+ QRMatrix::M_TIMING_DARK => true,
+ QRMatrix::M_FORMAT_DARK => true,
+ QRMatrix::M_VERSION_DARK => true,
+ QRMatrix::M_QUIETZONE_DARK => true,
+ QRMatrix::M_LOGO_DARK => true,
+ QRMatrix::M_FINDER_DOT => true,
];
/**
- * generates the output, optionally dumps it to a file, and returns it
+ * Map of module type => readable name (for CSS etc.)
+ *
+ * @var string[]
+ */
+ public const LAYERNAMES = [
+ // light
+ QRMatrix::M_NULL => 'null',
+ QRMatrix::M_DARKMODULE_LIGHT => 'darkmodule-light',
+ QRMatrix::M_DATA => 'data',
+ QRMatrix::M_FINDER => 'finder',
+ QRMatrix::M_SEPARATOR => 'separator',
+ QRMatrix::M_ALIGNMENT => 'alignment',
+ QRMatrix::M_TIMING => 'timing',
+ QRMatrix::M_FORMAT => 'format',
+ QRMatrix::M_VERSION => 'version',
+ QRMatrix::M_QUIETZONE => 'quietzone',
+ QRMatrix::M_LOGO => 'logo',
+ QRMatrix::M_FINDER_DOT_LIGHT => 'finder-dot-light',
+ // dark
+ QRMatrix::M_DARKMODULE => 'darkmodule',
+ QRMatrix::M_DATA_DARK => 'data-dark',
+ QRMatrix::M_FINDER_DARK => 'finder-dark',
+ QRMatrix::M_SEPARATOR_DARK => 'separator-dark',
+ QRMatrix::M_ALIGNMENT_DARK => 'alignment-dark',
+ QRMatrix::M_TIMING_DARK => 'timing-dark',
+ QRMatrix::M_FORMAT_DARK => 'format-dark',
+ QRMatrix::M_VERSION_DARK => 'version-dark',
+ QRMatrix::M_QUIETZONE_DARK => 'quietzone-dark',
+ QRMatrix::M_LOGO_DARK => 'logo-dark',
+ QRMatrix::M_FINDER_DOT => 'finder-dot',
+ ];
+
+ /**
+ * @var string
+ * @see \chillerlan\QRCode\Output\QROutputAbstract::toBase64DataURI()
+ * @internal do not call this constant from the interface, but rather from one of the child classes
+ */
+ public const MIME_TYPE = '';
+
+ /**
+ * Determines whether the given value is valid
+ *
+ * @param mixed $value
+ */
+ public static function moduleValueIsValid($value):bool;
+
+ /**
+ * Generates the output, optionally dumps it to a file, and returns it
+ *
+ * please note that the value of QROptions::$cachefile is already evaluated at this point.
+ * if the output module is invoked manually, it has no effect at all.
+ * you need to supply the $file parameter here in that case (or handle the option value in your custom output module).
+ *
+ * @see \chillerlan\QRCode\QRCode::renderMatrix()
*
* @return mixed
*/
- public function dump(string $file = null);
+ 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
index 3ed5153e1..2d6d052d6 100644
--- a/vendor/chillerlan/php-qrcode/src/Output/QRString.php
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRString.php
@@ -2,75 +2,110 @@
/**
* 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;
+use function implode, is_string, json_encode, max, min, sprintf;
+use const JSON_THROW_ON_ERROR;
/**
* Converts the matrix data into string types
+ *
+ * @deprecated 5.0.0 this class will be removed in future versions, use one of QRStringText or QRStringJSON instead
*/
class QRString extends QROutputAbstract{
- protected string $defaultMode = QRCode::OUTPUT_STRING_TEXT;
-
/**
* @inheritDoc
*/
- protected function setModuleValues():void{
+ public static function moduleValueIsValid($value):bool{
+ return is_string($value);
+ }
- foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
- $v = $this->options->moduleValues[$M_TYPE] ?? null;
+ /**
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):string{
+ return $value;
+ }
- if(!is_string($v)){
- $this->moduleValues[$M_TYPE] = $defaultValue
- ? $this->options->textDark
- : $this->options->textLight;
- }
- else{
- $this->moduleValues[$M_TYPE] = $v;
- }
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return ($isDark) ? '██' : '░░';
+ }
+ /**
+ * @inheritDoc
+ */
+ public function dump(?string $file = null):string{
+
+ switch($this->options->outputType){
+ case QROutputInterface::STRING_TEXT:
+ $data = $this->text();
+ break;
+ case QROutputInterface::STRING_JSON:
+ default:
+ $data = $this->json();
}
+ $this->saveToFile($data, $file);
+
+ return $data;
}
/**
* string output
*/
- protected function text(string $file = null):string{
- $str = [];
+ protected function text():string{
+ $lines = [];
+ $linestart = $this->options->textLineStart;
- foreach($this->matrix->matrix() as $row){
+ for($y = 0; $y < $this->moduleCount; $y++){
$r = [];
- foreach($row as $M_TYPE){
- $r[] = $this->moduleValues[$M_TYPE];
+ for($x = 0; $x < $this->moduleCount; $x++){
+ $r[] = $this->getModuleValueAt($x, $y);
}
- $str[] = implode('', $r);
+ $lines[] = $linestart.implode('', $r);
}
- return implode($this->options->eol, $str);
+ return implode($this->eol, $lines);
}
/**
* JSON output
+ *
+ * @throws \JsonException
*/
- protected function json(string $file = null):string{
- return json_encode($this->matrix->matrix());
+ protected function json():string{
+ return json_encode($this->matrix->getMatrix($this->options->jsonAsBooleans), JSON_THROW_ON_ERROR);
+ }
+
+ //
+
+ /**
+ * a little helper to create a proper ANSI 8-bit color escape sequence
+ *
+ * @see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
+ * @see https://en.wikipedia.org/wiki/Block_Elements
+ *
+ * @codeCoverageIgnore
+ */
+ public static function ansi8(string $str, int $color, ?bool $background = null):string{
+ $color = max(0, min($color, 255));
+ $background = ($background === true) ? 48 : 38;
+
+ return sprintf("\x1b[%s;5;%sm%s\x1b[0m", $background, $color, $str);
}
}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRStringJSON.php b/vendor/chillerlan/php-qrcode/src/Output/QRStringJSON.php
new file mode 100644
index 000000000..87ed2d7ff
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRStringJSON.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Class QRStringJSON
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function json_encode;
+
+/**
+ *
+ */
+class QRStringJSON extends QROutputAbstract{
+
+ public const MIME_TYPE = 'application/json';
+
+ /**
+ * @inheritDoc
+ * @throws \JsonException
+ */
+ public function dump(?string $file = null):string{
+ $matrix = $this->matrix->getMatrix($this->options->jsonAsBooleans);
+ $data = json_encode($matrix, $this->options->jsonFlags);
+
+ $this->saveToFile($data, $file);
+
+ return $data;
+ }
+
+ /**
+ * unused - required by interface
+ *
+ * @inheritDoc
+ * @codeCoverageIgnore
+ */
+ protected function prepareModuleValue($value):string{
+ return '';
+ }
+
+ /**
+ * unused - required by interface
+ *
+ * @inheritDoc
+ * @codeCoverageIgnore
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return '';
+ }
+
+ /**
+ * unused - required by interface
+ *
+ * @inheritDoc
+ * @codeCoverageIgnore
+ */
+ public static function moduleValueIsValid($value):bool{
+ return true;
+ }
+
+}
diff --git a/vendor/chillerlan/php-qrcode/src/Output/QRStringText.php b/vendor/chillerlan/php-qrcode/src/Output/QRStringText.php
new file mode 100644
index 000000000..a91591da7
--- /dev/null
+++ b/vendor/chillerlan/php-qrcode/src/Output/QRStringText.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Class QRStringText
+ *
+ * @created 25.10.2023
+ * @author smiley <smiley@chillerlan.net>
+ * @copyright 2023 smiley
+ * @license MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function array_map, implode, is_string, max, min, sprintf;
+
+/**
+ *
+ */
+class QRStringText extends QROutputAbstract{
+
+ public const MIME_TYPE = 'text/plain';
+
+ /**
+ * @inheritDoc
+ */
+ public static function moduleValueIsValid($value):bool{
+ return is_string($value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function prepareModuleValue($value):string{
+ return $value;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getDefaultModuleValue(bool $isDark):string{
+ return ($isDark) ? '██' : '░░';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function dump(?string $file = null):string{
+ $lines = [];
+ $linestart = $this->options->textLineStart;
+
+ foreach($this->matrix->getMatrix() as $row){
+ $lines[] = $linestart.implode('', array_map([$this, 'getModuleValue'], $row));
+ }
+
+ $data = implode($this->eol, $lines);
+
+ $this->saveToFile($data, $file);
+
+ return $data;
+ }
+
+ /**
+ * a little helper to create a proper ANSI 8-bit color escape sequence
+ *
+ * @see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
+ * @see https://en.wikipedia.org/wiki/Block_Elements
+ *
+ * @codeCoverageIgnore
+ */
+ public static function ansi8(string $str, int $color, ?bool $background = null):string{
+ $color = max(0, min($color, 255));
+ $background = ($background === true) ? 48 : 38;
+
+ return sprintf("\x1b[%s;5;%sm%s\x1b[0m", $background, $color, $str);
+ }
+
+}