chillerlan / php-qrcode

A PHP QR Code generator and reader with a user-friendly API.
https://smiley.codes/qrcode/
Apache License 2.0
2.02k stars 302 forks source link

SVG QR Code with circles having background #217

Closed rexwebmedia closed 1 year ago

rexwebmedia commented 1 year ago

Describe the feature

I want to add a background to circular modules with opacity and different color. I tried to edit the code to add squares behind the circles but that's not working. I also can't change color because many QR Code modules are in single element.

Code sample

<?php

require_once('vendor/autoload.php');

use chillerlan\QRCode\Common\EccLevel;
use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
use chillerlan\QRCode\Data\QRMatrix;
use chillerlan\QRCode\Output\{QROutputInterface, QRMarkupSVG};

// $imgLink = 'https://lifelinkup.com/wp-content/uploads/2023/08/download-1.jpeg';
$imgLink = 'jackson.jpeg';
$data = 'https://lifelinkup.com/memorials-tributes/jackson-martinez/';

class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG {

    protected function paths():string{
        $this->options->connectPaths = false;
        $svg = parent::paths();
        $svg = str_replace('"/>', $this->getFinderPatterns().'"/>', $svg);
        return $svg;
    }

    protected function path(string $path, int $M_TYPE):string{
        return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
    }

    protected function module(int $x, int $y, int $M_TYPE):string{
        if(
            $this->options->drawCircularModules &&
            !$this->matrix->checkTypeIn($x, $y, $this->options->keepAsSquare)
        ){
            $r = $this->options->circleRadius;
            // return sprintf(' M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z  M%1$s %2$s m-0.1,-0.5 h1 v1 h-1 v-1 Z', ($x + 0.5 - $r), ($y + 0.5), $r, ($r * 2));
            return sprintf('M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0z M%1$s %2$s m-0.1,-0.5 h1 v1 h-1 v-1 z ', ($x + 0.5 - $r), ($y + 0.5), $r, ($r * 2));
        }
        if(
            !$this->matrix->check($x, $y)
            || $this->matrix->checkType($x, $y, QRMatrix::M_FINDER)
            || $this->matrix->checkType($x, $y, QRMatrix::M_FINDER_DOT)
        ){
            return '';
        }

        // return sprintf('M%1$s %2$s m0.5,0.96 l-0.412,-0.412 a0.3 0.3 0 0 1 0.412,-0.435 a0.3 0.3 0 0 1 0.412,0.435Z', $x, $y);
        $r = $this->options->circleRadius;

        return sprintf('M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z', ($x + 0.5 - $r), ($y + 0.5), $r, ($r * 2));
    }

    protected function getFinderPatterns():string{
        $qz  = ($this->options->addQuietzone) ? $this->options->quietzoneSize : 0;
        $pos = [
            [(0 + $qz), (0 + $qz)],
            [(0 + $qz), ($this->moduleCount - $qz - 7)],
            [($this->moduleCount - $qz - 7), (0 + $qz)],
        ];

        $path = 'M%1$s,%2$s';
        $path .= ' M%1$s,%2$s m3.5,0';
        $path .= ' q3.5,0 3.5,3.5';
        $path .= ' q0,3.5 -3.5,3.5';
        $path .= ' q-3.5,0 -3.5,-3.5';
        $path .= ' q0,-3.5 3.5,-3.5z';
        $path .= ' m0,1';
        $path .= ' q-2.5,0 -2.5,2.5';
        $path .= ' q0,2.5 2.5,2.5';
        $path .= ' q2.5,0 2.5,-2.5';
        $path .= ' q0,-2.5 -2.5,-2.5z';

        $path .= ' m-1.5,2.5';
        $path .= ' a1.5,1.5 0 1 0 3,0';
        $path .= ' a1.5,1.5 0 1 0 -3,0Z';
        $finder = [];

        foreach($pos as [$ix, $iy]){
            $finder[] = sprintf($path, $ix, $iy);
        }
        return implode(' ', $finder);
    }
}

$options = new QROptions;
$options->version = 7;
$options->eccLevel = EccLevel::H;
$options->outputType          = QROutputInterface::MARKUP_SVG;
$options->outputBase64        = false;
$options->drawLightModules    = true;
$options->markupDark          = '';
$options->markupLight         = '';

$options->outputType      = QROutputInterface::CUSTOM;
$options->outputInterface = QRSvgWithLogoAndCustomShapes::class;

$options->drawCircularModules = true;
$options->circleRadius        = 0.4;
$options->quietzoneSize = 2;

$options->connectPaths        = false;
$options->keepAsSquare        = [
    QRMatrix::M_FINDER_DARK,
    QRMatrix::M_FINDER_DOT,
    QRMatrix::M_FINDER_DOT,
    QRMatrix::M_ALIGNMENT_DARK,
];
$options->svgDefs = '
    <linearGradient id="rainbow" x1="1" y2="1">
        <stop stop-color="#e2453c" offset="0"/>
        <stop stop-color="#e07e39" offset="0.2"/>
        <stop stop-color="#e5d667" offset="0.4"/>
        <stop stop-color="#51b95b" offset="0.6"/>
        <stop stop-color="#1e72b7" offset="0.8"/>
        <stop stop-color="#6f5ba7" offset="1"/>
    </linearGradient>
    <style><![CDATA[
        .dark{fill: #212121;}
        .light{fill:#ffffff;}
    ]]></style>';

try {
    $out = (new QRCode($options))->render($data);
}
catch(Throwable $e){
    exit($e->getMessage());
}

header('Content-type: image/svg+xml');

$imageContent = file_get_contents($imgLink);
$base64Image = base64_encode($imageContent);
$base64Image = 'data:image/jpeg;base64,' . $base64Image;

$modifiedSvgContent = $out;
$modifiedSvgContent = str_replace('<svg', '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ', $modifiedSvgContent);
$modifiedSvgContent = str_replace('<defs>', '<image x="0" y="0" width="100%" height="100%" style="opacity:0.6" xlink:href="'. $base64Image .'" /><defs>', $modifiedSvgContent);

echo $modifiedSvgContent;
file_put_contents('img/sample-1.svg', $modifiedSvgContent);

exit;

Additional context

Please help me little bit to create something like this:

image

Here's what I was able to do:

image

Are you (the requester) willing to submit a pull request for that feature?

No

codemasher commented 1 year ago

Hi, I'm the author of this library, but I'm not an SVG expert, nor the general support hotline. From what it looks, you need to put the image as a background of the SVG element via CSS.