SVG QR Code with logo example

This commit is contained in:
smiley
2022-03-05 19:31:38 +01:00
parent e8ac9e02a4
commit 40604f4698
3 changed files with 176 additions and 0 deletions
+72
View File
@@ -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
View File
@@ -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>

After

Width:  |  Height:  |  Size: 822 B

+103
View File
@@ -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;