From d650fe73067c2559519f09949e9b71b466ab2bee Mon Sep 17 00:00:00 2001 From: smiley Date: Wed, 1 Feb 2023 21:40:30 +0100 Subject: [PATCH] :octocat: updated round quietzone SVG example, added logo and fancy --- examples/svgRoundQuietzone.php | 185 +++++++++++++++++++++++++++------ 1 file changed, 155 insertions(+), 30 deletions(-) diff --git a/examples/svgRoundQuietzone.php b/examples/svgRoundQuietzone.php index 5fedfeba8..8d534c1b3 100644 --- a/examples/svgRoundQuietzone.php +++ b/examples/svgRoundQuietzone.php @@ -13,7 +13,7 @@ use chillerlan\QRCode\Common\EccLevel; use chillerlan\QRCode\Data\QRMatrix; use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG}; -use chillerlan\QRCode\{QRCode, QROptions}; +use chillerlan\QRCode\{QRCode, QRCodeException, QROptions}; require_once __DIR__.'/../vendor/autoload.php'; @@ -22,7 +22,8 @@ require_once __DIR__.'/../vendor/autoload.php'; */ /** - * the extended SVG output module + * Create SVG QR Codes with embedded logos (that are also SVG), + * randomly colored dots and a round quiet zone with added circle */ class RoundQuietzoneSVGoutput extends QRMarkupSVG{ @@ -42,6 +43,11 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{ // color the quiet zone $this->colorQuietzone($quietzoneSize, $diameter / 2); + // calculate the logo space + $logoSpaceSize = (int)ceil($this->moduleCount * $this->options->svgLogoScale); + // we're calling QRMatrix::setLogoSpace() manually, so QROptions::$addLogoSpace has no effect here + $this->matrix->setLogoSpace($logoSpaceSize); + // start SVG output $svg = $this->header(); @@ -50,6 +56,7 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{ } $svg .= $this->paths(); + $svg .= $this->getLogo(); $svg .= $this->addCircle($diameter / 2); // close svg @@ -143,6 +150,66 @@ class RoundQuietzoneSVGoutput extends QRMarkupSVG{ ); } + /** + * returns a element that contains the SVG logo and positions it properly within the QR Code + * + * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g + * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform + */ + protected function getLogo():string{ + // @todo: customize the element to your liking (css class, style...) + return sprintf( + '%5$s%5$s %4$s%5$s', + ($this->moduleCount - ($this->moduleCount * $this->options->svgLogoScale)) / 2, + $this->options->svgLogoScale, + $this->options->svgLogoCssClass, + file_get_contents($this->options->svgLogo), + $this->options->eol + ); + } + + /** + * @inheritDoc + */ + protected function collectModules(Closure $transform):array{ + $paths = []; + + // collect the modules for each type + foreach($this->matrix->matrix() as $y => $row){ + foreach($row as $x => $M_TYPE){ + $M_TYPE_LAYER = $M_TYPE; + + if($this->matrix->checkTypeNotIn($x, $y, $this->options->excludeFromConnect)){ + // to connect paths we'll redeclare the $M_TYPE_LAYER to data only + $M_TYPE_LAYER = QRMatrix::M_DATA; + + if($this->matrix->check($x, $y)){ + $M_TYPE_LAYER |= QRMatrix::IS_DARK; + } + } + + // randomly assign another $M_TYPE_LAYER for the given types + // note that the layer id has to be an integer value, + // ideally outside the several bitmask values + if($M_TYPE_LAYER === (QRMatrix::M_DATA | QRMatrix::IS_DARK)){ + $M_TYPE_LAYER = array_rand($this->options->dotColors); + } + + // collect the modules per $M_TYPE + $module = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER); + + if(!empty($module)){ + $paths[$M_TYPE_LAYER][] = $module; + } + } + } + + // beautify output + ksort($paths); + + return $paths; + } + } /** @@ -164,6 +231,37 @@ class RoundQuietzoneOptions extends QROptions{ */ protected int $additionalModules = 0; + /** + * a map of $M_TYPE_LAYER => color + * + * @see \array_rand() + */ + protected array $dotColors = []; + + // path to svg logo + protected string $svgLogo; + // logo scale in % of QR Code size, clamped to 10%-30% + protected float $svgLogoScale = 0.20; + // css class for the logo (defined in $svgDefs) + protected string $svgLogoCssClass = ''; + + // check logo + protected function set_svgLogo(string $svgLogo):void{ + + if(!file_exists($svgLogo) || !is_readable($svgLogo)){ + throw new QRCodeException('invalid svg logo'); + } + + // @todo: validate svg + + $this->svgLogo = $svgLogo; + } + + // clamp logo scale + protected function set_svgLogoScale(float $svgLogoScale):void{ + $this->svgLogoScale = max(0.05, min(0.3, $svgLogoScale)); + } + } @@ -171,34 +269,24 @@ class RoundQuietzoneOptions extends QROptions{ * Runtime */ -$options = new RoundQuietzoneOptions([ - 'additionalModules' => 5, +$dotColors = [ + 111 => '#e2453c', + 222 => '#e07e39', + 333 => '#e5d667', + 444 => '#51b95b', + 555 => '#1e72b7', + 666 => '#6f5ba7', +]; - 'version' => 7, - 'eccLevel' => EccLevel::H, // maximum error correction capacity, esp. for print - 'addQuietzone' => false, // we're not adding a quiet zone, this is done internally in our own module - 'imageBase64' => false, // avoid base64 URI output - 'outputType' => QROutputInterface::CUSTOM, - 'outputInterface' => RoundQuietzoneSVGoutput::class, // load our own output class - 'drawLightModules' => false, // set to true to add the light modules +$layerColors = ''; - 'connectPaths' => true, - 'excludeFromConnect' => [ - QRMatrix::M_FINDER|QRMatrix::IS_DARK, - QRMatrix::M_FINDER_DOT|QRMatrix::IS_DARK, - QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK, - QRMatrix::M_QUIETZONE|QRMatrix::IS_DARK - ], +foreach($dotColors as $layer => $color){ + $layerColors .= sprintf("\n\t\t.qr-%s{ fill: %s; }", $layer, $color); +} - 'drawCircularModules' => true, - 'circleRadius' => 0.4, - 'keepAsSquare' => [ - QRMatrix::M_FINDER|QRMatrix::IS_DARK, - QRMatrix::M_FINDER_DOT|QRMatrix::IS_DARK, - QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK, - ], - // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient - 'svgDefs' => ' +// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient +// please forgive me for I have committed colorful crimes +$svgDefs = ' @@ -213,11 +301,48 @@ $options = new RoundQuietzoneOptions([ ', + '.$layerColors.' + ]]>'; + +$options = new RoundQuietzoneOptions([ + // custom dot options + 'additionalModules' => 5, + 'dotColors' => $dotColors, + + // custom SVG logo options (see extended class below) + 'svgLogo' => __DIR__.'/github.svg', // logo from: https://github.com/simple-icons/simple-icons + 'svgLogoScale' => 0.2, + 'svgLogoCssClass' => 'logo', + + // common QRCode options + 'version' => 7, + 'eccLevel' => EccLevel::H, // maximum error correction capacity, esp. for print + 'addQuietzone' => false, // we're not adding a quiet zone, this is done internally in our own module + 'imageBase64' => false, // avoid base64 URI output + 'outputType' => QROutputInterface::CUSTOM, + 'outputInterface' => RoundQuietzoneSVGoutput::class, // load our own output class + 'drawLightModules' => false, // set to true to add the light modules + + // common SVG options + 'svgDefs' => $svgDefs, +// 'connectPaths' => true, // this has been set to "always on" internally + 'excludeFromConnect' => [ + QRMatrix::M_FINDER|QRMatrix::IS_DARK, + QRMatrix::M_FINDER_DOT|QRMatrix::IS_DARK, + QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK, + QRMatrix::M_QUIETZONE|QRMatrix::IS_DARK + ], + 'drawCircularModules' => true, + 'circleRadius' => 0.4, + 'keepAsSquare' => [ + QRMatrix::M_FINDER|QRMatrix::IS_DARK, + QRMatrix::M_FINDER_DOT|QRMatrix::IS_DARK, + QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK, + ], ]); $qrcode = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');