Просмотр исходного кода

:octocat: updated round quietzone SVG example, added logo and fancy

smiley 2 лет назад
Родитель
Сommit
d650fe7306
1 измененных файлов с 155 добавлено и 30 удалено
  1. 155 30
      examples/svgRoundQuietzone.php

+ 155 - 30
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 <g> 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 <g> element to your liking (css class, style...)
+		return sprintf(
+			'%5$s<g transform="translate(%1$s %1$s) scale(%2$s)" class="%3$s">%5$s	%4$s%5$s</g>',
+			($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 = '
 	<linearGradient id="blurple" x1="100%" y2="100%">
 		<stop stop-color="#D70071" offset="0"/>
 		<stop stop-color="#9C4E97" offset="0.5"/>
@@ -213,11 +301,48 @@ $options = new RoundQuietzoneOptions([
 		<stop stop-color="#6f5ba7" offset="97.5%"/>
 	</linearGradient>
 	<style><![CDATA[
-		.dark{ fill: url(#rainbow); }
 		.light{ fill: #dedede; }
-		.qr-2304{ fill: url(#blurple); }
+		.dark{ fill: url(#rainbow); }
+		.logo{ fill: url(#blurple); }
 		#circle{ fill: none; stroke: url(#blurple); }
-	]]></style>',
+		'.$layerColors.'
+	]]></style>';
+
+$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');