Kaynağa Gözat

:sparkles: round qrcode example

smiley 3 yıl önce
ebeveyn
işleme
f6f9858359
2 değiştirilmiş dosya ile 202 ekleme ve 0 silme
  1. 1 0
      examples/Readme.md
  2. 201 0
      examples/svgRoundQuietzone.php

+ 1 - 0
examples/Readme.md

@@ -20,3 +20,4 @@
 - [GD image with text](./imageWithText.php): description text under the QR Code
 - [SVG with logo](./svgWithLogo.php): an SVG QR Code with embedded logo (that is also SVG)
 - [SVG with randomly colored modules](./svgRandomColoredDots.php): a visual effect using multiple colors for the matrix modules
+- [SVG with a round shape and randomly filled quiet zone](./svgRoundQuietzone.php): example similar to the QR Codes of a certain vendor

+ 201 - 0
examples/svgRoundQuietzone.php

@@ -0,0 +1,201 @@
+<?php
+/**
+ * svgRoundQuietzone.php
+ *
+ * @created      09.07.2022
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2022 smiley
+ * @license      MIT
+ */
+
+use chillerlan\QRCode\Common\EccLevel;
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Output\QRMarkupSVG;
+use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
+
+require_once __DIR__.'/../vendor/autoload.php';
+
+/*
+ * Class definition
+ */
+
+class RoundQuietzoneSVGoutput extends QRMarkupSVG{
+
+	/**
+	 * @inheritDoc
+	 */
+	protected function createMarkup(bool $saveToFile):string{
+		// some Pythagorean magick
+		$diameter      = sqrt(2 * pow($this->moduleCount + $this->options->additionalModules, 2));
+		$quietzoneSize = (int)ceil(($diameter - $this->moduleCount) / 2) + 1;
+		// add the quiet zone to fill the circle
+		$this->matrix->setQuietZone($quietzoneSize);
+		// update the matrix dimensions to avoid errors in subsequent calculations
+		$this->setMatrixDimensions();
+		// color the quiet zone
+		$this->colorQuietzone($quietzoneSize, $diameter / 2);
+
+		// start SVG output
+		$svg = $this->header();
+
+		if(!empty($this->options->svgDefs)){
+			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
+		}
+
+		$svg .= $this->paths();
+		$svg .= $this->addCircle($diameter / 2);
+
+		// close svg
+		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
+
+		// transform to data URI only when not saving to file
+		if(!$saveToFile && $this->options->imageBase64){
+			$svg = $this->base64encode($svg, 'image/svg+xml');
+		}
+
+		return $svg;
+	}
+
+	protected function colorQuietzone(int $quietzoneSize, float $radius):void{
+		$l1 = $quietzoneSize - 1;
+		$l2 = $this->moduleCount - $quietzoneSize;
+
+		foreach($this->matrix->matrix() as $y => $row){
+			foreach($row as $x => $value){
+
+				// skip anything that's not quiet zone
+				if($value !== QRMatrix::M_QUIETZONE){
+					continue;
+				}
+
+				// leave one row of quiet zone around the matrix
+				if(
+					($x === $l1 && $y >= $l1 && $y <= $l2)
+					|| ($x === $l2 && $y >= $l1 && $y <= $l2)
+					|| ($y === $l1 && $x > $l1 && $x < $l2)
+					|| ($y === $l2 && $x > $l1 && $x < $l2)
+				){
+					continue;
+				}
+
+				if($this->checkIfInsideCircle($x, $y, $radius)){
+					$this->matrix->set($x, $y, (bool)rand(0, 1), QRMatrix::M_QUIETZONE);
+				}
+			}
+		}
+
+	}
+
+	/**
+	 * @see https://stackoverflow.com/a/7227057
+	 */
+	protected function checkIfInsideCircle(int $x, int $y, float $radius):bool{
+		// we need to add 0.5 units since we're calculating the element centers ($x/$y is the element's assumed top left corner)
+		$dx = abs($x + 0.5 - $this->moduleCount / 2);
+		$dy = abs($y + 0.5 - $this->moduleCount / 2);
+		$radius -= $this->options->circleRadius * 2;
+
+		if($dx + $dy <= $radius){
+			return true;
+		}
+
+		if($dx > $radius || $dy > $radius){
+			return false;
+		}
+
+		if(pow($dx, 2) + pow($dy, 2) <= pow($radius, 2)){
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * add a solid circle around the matrix
+	 */
+	protected function addCircle(float $radius):string{
+		return sprintf(
+			'<circle id="circle" cx="%1$s" cy="%1$s" r="%2$s" stroke-width="%3$s"/>',
+			$this->moduleCount / 2,
+			round($radius, 5),
+			2 * $this->options->circleRadius
+		);
+	}
+
+}
+
+class RoundQuietzoneOptions extends QROptions{
+
+	protected int $additionalModules = 5;
+
+}
+
+
+/*
+ * Runtime
+ */
+
+$options = new RoundQuietzoneOptions([
+	'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'          => QRCode::OUTPUT_CUSTOM,
+	'outputInterface'     => RoundQuietzoneSVGoutput::class, // load our own output class
+	'markupDark'          => '', // avoid "fill" attributes on paths
+	'markupLight'         => '',
+	'imageTransparent'    => true, // set to false to add the light modules
+
+	'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
+	],
+
+	'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'             => '
+	<linearGradient id="blurple" x1="100%" y2="100%">
+		<stop stop-color="#D70071" offset="0"/>
+		<stop stop-color="#9C4E97" offset="0.5"/>
+		<stop stop-color="#0035A9" offset="1"/>
+	</linearGradient>
+	<linearGradient id="rainbow" x1="100%" y2="100%">
+		<stop stop-color="#e2453c" offset="2.5%"/>
+		<stop stop-color="#e07e39" offset="21.5%"/>
+		<stop stop-color="#e5d667" offset="40.5%"/>
+		<stop stop-color="#51b95b" offset="59.5%"/>
+		<stop stop-color="#1e72b7" offset="78.5%"/>
+		<stop stop-color="#6f5ba7" offset="97.5%"/>
+	</linearGradient>
+	<style><![CDATA[
+		.dark{ fill: url(#rainbow); }
+		.light{ fill: #dedede; }
+		.qr-2304{ fill: url(#blurple); }
+		#circle{ fill: none; stroke: url(#blurple); }
+	]]></style>',
+]);
+
+$qrcode = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
+
+if(php_sapi_name() !== 'cli'){
+	header('Content-type: image/svg+xml');
+
+	if(extension_loaded('zlib')){
+		header('Vary: Accept-Encoding');
+		header('Content-Encoding: gzip');
+		$qrcode = gzencode($qrcode, 9);
+	}
+}
+
+echo $qrcode;
+
+exit;