ソースを参照

:sparkles: SVG QR Code with logo example

smiley 3 年 前
コミット
40604f4698
3 ファイル変更176 行追加0 行削除
  1. 72 0
      examples/QRSvgWithLogo.php
  2. 1 0
      examples/github.svg
  3. 103 0
      examples/svgWithLogo.php

+ 72 - 0
examples/QRSvgWithLogo.php

@@ -0,0 +1,72 @@
+<?php
+/**
+ * Class QRSvgWithLogo
+ *
+ * @created      05.03.2022
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2022 smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCodeExamples;
+
+use chillerlan\QRCode\Output\QRMarkup;
+use function file_get_contents, sprintf;
+
+/**
+ * Create SVG QR Codes with embedded logos (that are also SVG)
+ */
+class QRSvgWithLogo extends QRMarkup{
+
+	/**
+	 * SVG output
+	 *
+	 * @see https://github.com/codemasher/php-qrcode/pull/5
+	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
+	 * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
+	 */
+	protected function svg(bool $saveToFile):string{
+		$size = (int)ceil($this->moduleCount * $this->options->svgLogoScale);
+
+		// we're calling QRMatrix::setLogoSpace() manually, so QROptions::$addLogoSpace has no effect here
+		$this->matrix->setLogoSpace($size, $size);
+
+		$svg = $this->svgHeader();
+
+		if(!empty($this->options->svgDefs)){
+			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
+		}
+
+		$svg .= $this->svgPaths();
+		$svg .= $this->getLogo();
+
+		// 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;
+	}
+
+	/**
+	 * 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
+		);
+	}
+
+}

+ 1 - 0
examples/github.svg

@@ -0,0 +1 @@
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>

+ 103 - 0
examples/svgWithLogo.php

@@ -0,0 +1,103 @@
+<?php
+/**
+ * @created      05.03.2022
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2022 smiley
+ * @license      MIT
+ *
+ * @noinspection PhpComposerExtensionStubsInspection
+ */
+
+namespace chillerlan\QRCodeExamples;
+
+use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
+use chillerlan\QRCode\Data\QRMatrix;
+use chillerlan\QRCode\Common\EccLevel;
+use function file_exists, gzencode, header, is_readable, max, min;
+
+require_once __DIR__.'/../vendor/autoload.php';
+
+$data = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
+$gzip = true;
+
+$options_arr = [
+	// SVG logo options (see extended class below)
+	'svgLogo'             => __DIR__.'/github.svg', // logo from: https://github.com/simple-icons/simple-icons
+	'svgLogoScale'        => 0.25,
+	'svgLogoCssClass'     => 'dark',
+	// QROptions
+	'version'             => 5,
+	'outputType'          => QRCode::OUTPUT_CUSTOM,
+	'outputInterface'     => QRSvgWithLogo::class,
+	'imageBase64'         => false,
+	// ECC level H is necessary when using logos
+	'eccLevel'            => EccLevel::H,
+	'addQuietzone'        => true,
+	// if set to true, the light modules won't be rendered
+	'imageTransparent'    => false,
+	// empty the default value to remove the fill* attributes from the <path> elements
+	'markupDark'          => '',
+	'markupLight'         => '',
+	// draw the modules as circles isntead of squares
+	'drawCircularModules' => true,
+	'circleRadius'        => 0.45,
+	// connect paths
+	'svgConnectPaths'     => true,
+	// keep modules of thhese types as square
+	'keepAsSquare'        => [
+		QRMatrix::M_FINDER|QRMatrix::IS_DARK,
+		QRMatrix::M_FINDER_DOT,
+		QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK,
+	],
+	// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
+	'svgDefs'             => '
+	<linearGradient id="gradient" x1="100%" y2="100%">
+		<stop stop-color="#D70071" offset="0"/>
+		<stop stop-color="#9C4E97" offset="0.5"/>
+		<stop stop-color="#0035A9" offset="1"/>
+	</linearGradient>
+	<style><![CDATA[
+		.dark{fill: url(#gradient);}
+		.light{fill: #eaeaea;}
+	]]></style>',
+];
+
+// augment the QROptions class
+$options = new class ($options_arr) extends QROptions{
+	// 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));
+	}
+
+};
+
+$qrcode = (new QRCode($options))->render($data);
+
+header('Content-type: image/svg+xml');
+
+if($gzip){
+	header('Vary: Accept-Encoding');
+	header('Content-Encoding: gzip');
+	$qrcode = gzencode($qrcode, 9);
+}
+
+echo $qrcode;