diff --git a/examples/QRSvgWithLogo.php b/examples/QRSvgWithLogo.php new file mode 100644 index 000000000..fcfa8580c --- /dev/null +++ b/examples/QRSvgWithLogo.php @@ -0,0 +1,72 @@ + + * @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('%1$s%2$s%2$s', $this->options->svgDefs, $this->options->eol); + } + + $svg .= $this->svgPaths(); + $svg .= $this->getLogo(); + + // close svg + $svg .= sprintf('%1$s%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 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 + ); + } + +} diff --git a/examples/github.svg b/examples/github.svg new file mode 100644 index 000000000..538ec5bf2 --- /dev/null +++ b/examples/github.svg @@ -0,0 +1 @@ +GitHub \ No newline at end of file diff --git a/examples/svgWithLogo.php b/examples/svgWithLogo.php new file mode 100644 index 000000000..b92c30e35 --- /dev/null +++ b/examples/svgWithLogo.php @@ -0,0 +1,103 @@ + + * @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 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' => ' + + + + + + ', +]; + +// 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;