chillerlan / php-qrcode

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

QRCode image with logo and colors not working #69

Closed crustamet closed 3 years ago

crustamet commented 3 years ago

It return the image black and white and not with colors as shown in examples

    $options = new QRCodeLogoOptions;

    $options->version          = 5;
    $options->eccLevel         = QRCode::ECC_H;
    $options->imageBase64      = false;
    $options->logoSpaceWidth   = 13;
    $options->logoSpaceHeight  = 13;
    $options->scale            = 5;
    $options->imageTransparent = false;

    $options2 = new QROptions([
        'version'      => 7,
        'outputType'   => QRCode::OUTPUT_IMAGICK,
        'eccLevel'     => QRCode::ECC_H,
        'scale'        => 5,
        'moduleValues' => [
            // finder
            1536 => '#A71111', // dark (true)
            6    => '#FFBFBF', // light (false)
            // alignment
            2560 => '#A70364',
            10   => '#FFC9C9',
            // timing
            3072 => '#98005D',
            12   => '#FFB8E9',
            // format
            3584 => '#003804',
            14   => '#00FB12',
            // version
            4096 => '#650098',
            16   => '#E0B8FF',
            // data
            1024 => '#4A6000',
            4    => '#ECF9BE',
            // darkmodule
            512  => '#080063',
            // separator
            8    => '#DDDDDD',
            // quietzone
            18   => '#DDDDDD',
        ],
    ]);
    header('Content-type: image/png');
    // print_r((new QRCode($options2))->render($data));die();
    $qrOutputInterface = new QRCodeLogo($options, (new QRCode($options2))->getMatrix($data));

    echo $qrOutputInterface->dump(null, FCPATH.'/image.png');
    die();
codemasher commented 3 years ago

You're invoking the output interface with a different set of options - you should use the same options instance for both invocations, that's how it's done internally.

$options = new QRCodeLogoOptions;
// ... set ALL options on this instance

// invoke
$qrOutputInterface = new QRCodeLogo($options, (new QRCode($options))->getMatrix($data));

Also, you can simplify the whole thing by using QROptions::outputInterface:

$options = new QRCodeLogoOptions;
// ...
$options->outputType      = QRCode::OUTPUT_CUSTOM;
$options->outputInterface = QRCodeLogo::class;

(new QRCode($options))->render($data, FCPATH.'/image.png');
crustamet commented 3 years ago

Thanks, i found what i was doing wrong. The module values needs RGB values instead of hex colors like this.

In fact there is two problems here, why same options can send twice to qrcode library instead of once This make confusion while you think you can make different options

The second one is if I use different options this (new QRCode($options))->getMatrix($data)) only returns the matrix not already formated, if i change colors i get same matrix

And another problem that i am trying to figure out is how can i make an SVG with my logo xD.

crustamet commented 3 years ago

Also the outputType does not work because the Logo extends from QRImage class :( I wanted somehow to extend from QRMarkup xD for the svg somehow

codemasher commented 3 years ago

There's absolutely no reason to invoke more than one options instance for the same qrcode/output instance like in your code above - that example is even using different version numbers - it cannot work properly. The custom output example i posted will always work, regardless from what your output module inherits from; in case you're extending from one of the built-in output modules, you will need to use the proper values for that class, which are described here in the wiki. I think you've seen that logos are supported. The reason why the Markup and String output don't support logos is that you would need to convert the logo to a vector image or (in case of HTML) embed the image in the empty space with an extra <img> tag, both of which is messy, so for logos you will need to use GD or ImageMagick.

crustamet commented 3 years ago

But i have the svg file for the logo, i am wondering if i am stupid enough not to know how to generate a qrcode svg with my svg logo file.

I just want to know if this is possible with this current version of your library or i might need to go somewhere else ?

codemasher commented 3 years ago

It surely is possible. This is probably what you're looking for. In order to do that, you'd just need to add your code in the (extended) QRMarkup::svg() method over here, before closing the <svg> tag and position it over the empty space.

crustamet commented 3 years ago

It is possible to have a demo for this ? :D i give you a lot of thanks if you can have a demo

codemasher commented 3 years ago

Following the first SO answer, you'd need to add the following:

<image x="10" y="20" width="80" height="80" href="recursion.svg" />

which in PHP basically translates to:

$svg .= '<image x="'.$logoX.'" y="'.$logoY.'" width="'.$logoWidth.'" height="'.$logoHeight.'"
 href="data:image/svg+xml;base64,'.base64_encode($svgLogo).'" />';
crustamet commented 3 years ago

Ok so it worked i have what i want but there is one problem

    $ql = $this->matrix->size(); // 53

    $lw = ($ql / 2)-6;
    $lh = ($ql / 2)-5;

    $logo_svg = '<image x="'.$lw.'" y="'.$lh.'" width="'.$this->options->logoSpaceWidth.'" height="'.$this->options->logoSpaceHeight.'" href="/'.$logo.'" />';

for the first two variables $lw and $lh i don't know how to calculate exactly the position. so i put manually to fit inside it it is weird but it works even if i scale up or down the svg file

Do you know how to get those dimensions ? because if i change now the size of the logo overlaps the qrcode and i need to readjust these vars

crustamet commented 3 years ago

I can upload an example for what i did if i somehow calculate those vars

codemasher commented 3 years ago

I think all you need to know is in this example. This is how you calculate the matrix size of the QR Code (+2 * quiet zone size, 4 modules by default), multiplied by QROptions::$scale you get the size in pixels.

crustamet commented 3 years ago

No dude the SVG MARKUP doesnt care about the scale, in fact that option for the svg does not work

    $options = new QRCodeLogoOptions;

    $options->version          = 9;
    $options->outputType       = QRCode::OUTPUT_MARKUP_SVG;
    $options->eccLevel         = QRCode::ECC_H;
    $options->outputInterface  = QRCodeLogo::class;
    $options->imageBase64      = false;
    $options->logoSpaceWidth   = 12;
    $options->logoSpaceHeight  = 10;
    // $options->svgViewBoxSize   = 150;
    $options->addQuietzone     = true;
    $options->cssClass         = 'my-css-class';
    $options->svgOpacity       = 1.0;

AND inside the Logo class

    $this->options->returnResource = true;

    if(!is_file($logo) || !is_readable($logo)){
        throw new QRCodeOutputException('invalid logo');
    }

    $this->matrix->setLogoSpace(
        $this->options->logoSpaceWidth,
        $this->options->logoSpaceHeight
        // not utilizing the position here
    );

    // there's no need to save the result of dump() into $this->image here
    $this->image = parent::dump($file);

    $ql = $this->matrix->size();

    $lw = ($ql / 2)-6;
    $lh = ($ql / 2)-5;

    $data = file_get_contents($logo);

    $logo_svg = '<image x="'.$lw.'" y="'.$lh.'" width="'.$this->options->logoSpaceWidth.'" height="'.$this->options->logoSpaceHeight.'" href="data:image/svg+xml;base64,'.base64_encode($data).'" />';
    $this->image = str_replace('</svg>', $logo_svg.'</svg>', $this->image);
codemasher commented 3 years ago

First, don't "DUDE" me, second, why do you want me to do your job?`We're talking about basic algebra here.

crustamet commented 3 years ago

Ok i'm sorry, and yes basic algebra but for the svg, i just wanted to make the library better, for the most part i will never change the logo so for me the job is done.

The basic algebra does not apply if you put the svg logo inside the generated svg. the width of the logo is the same as in the options because it is an svg file that scales automatically

The only problem is where to put the logo, anyway i will check this later. Thanks for the help i really appreciate it if you wouldn't say is possible to make svg with logo i would never tried.

codemasher commented 3 years ago

Look, i'm not an expert in SVG and it requires for me the same effort to google (in my free time) as it would for you. I can help you with internals of this library - that is all. If SVG wasn't this messy, i had already implemented a proper solution to add logos for it - unlike GD and Imagick, there's no proper "one fits all" soultion. This, and the reason that logos are usually only a commercial feature are reasons why i didn't.

crustamet commented 3 years ago

Yes i understand you, but i am telling you i had the job 90% done.

The only problem is these vars

$lw = ($ql / 2)-6;
$lh = ($ql / 2)-5;

I believe these vars must be exactly how you space out the logo when you create the matrix with the empty space for the logo In svg format, these vars must be the same values as in where you empty the svg with coordonates.

I don't know how can i get these vars from your library

This function :

$this->matrix->setLogoSpace(
    $this->options->logoSpaceWidth,
    $this->options->logoSpaceHeight
);
crustamet commented 3 years ago

As i see inside that function somewhere down the line you asign

    // determine start coordinates
    $startX = ($startX !== null ? $startX : ($length - $width) / 2) + $qz;
    $startY = ($startY !== null ? $startY : ($length - $height) / 2) + $qz;

These vars, these are exactly what i need.

Now i'm starting to understand it better :D After i test it can i make a push request ? with my test ?

codemasher commented 3 years ago

As i mentioned before, since there's not "the one" solution for SVG, i don't want to add it yet. You can instead post your solution here and i'll move this issue to discussions under Q&A.