Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compress QR code #102

Open
maxpelic opened this issue Jul 17, 2021 · 3 comments
Open

Compress QR code #102

maxpelic opened this issue Jul 17, 2021 · 3 comments

Comments

@maxpelic
Copy link

The QR code generator function does not compress the data at all, changing it to something like

    {
        $width = $this->getModuleCount() * $size;
        $height = $width;
        print('<svg width="' . $width . '" height="' . $height . '" xmlns="http://www.w3.org/2000/svg"><path d="');
        for ($r = 0; $r < $this->getModuleCount(); $r++) {
            for ($c = 0; $c < $this->getModuleCount(); $c++) {
                if(!$this->isDark($r, $c)) continue;
                print('M' . ($c * $size) . ' ' . ($r * $size) . "h{$size}v{$size}h-{$size}z");
            }
        }

        print("\"></path></svg>");
    }

makes the output data waaaaay smaller while still printing the same thing (it has a transparent background but that could easily be changed by just adding a white rect first).

@maxpelic
Copy link
Author

Not that anyone cares but I got it even more compressed...

    public function printSVG($size = 2)
    {
        $width = $this->getModuleCount() * $size;
        $height = $width;
        print('<svg width="' . $width . '" height="' . $height . '" viewbox="0 0 ' . $this->getModuleCount() . ' ' . $this->getModuleCount() . '" xmlns="http://www.w3.org/2000/svg"><path d="');
        
        for ($r = 0; $r < $this->getModuleCount(); $r++) {
            print('M0 ' . $r);
            $mx = $wtrack = 0;
            for ($c = 0; $c < $this->getModuleCount(); $c++) {
                if($this->isDark($r, $c)){
                    $wtrack++;
                    continue;
                }
                if(!$wtrack){
                    $mx++;
                    continue;
                }
                
                print('h' . ($mx) . "v1h" . ($wtrack) . "v-1");
                $mx = 1;
                $wtrack = 0;
            }
            if($wtrack){
                print('h' . ($mx) . "v1h" . ($wtrack) . "v-1");
                $wtrack = 0;
            }
            print('z');
        }

        print("\"></path></svg>");
    }

@RIMhosting
Copy link

RIMhosting commented Jan 2, 2022

In case someone cares...
SVG-output can be compressed still more to about 15 times smaller than the 'official version' of printSVG.
Just draw horizontal lines $size pixels wide - avoiding all filled rectangles! - and leave-out all defaults and unnecessary spaces.
This function has additional parameters (title and colors to support transparency, margin, border) and returns a html svg-tag which can subsequently be echoed, printed or stored in a db, etc.
Also included a few efficiency improvements (reducing number of computations inside loops).

    public function createSVG($title='', $size=2, $color='black/white') {
        $size = max(2, $size);

        //  $color usage: fgc OR fgc/bgc OR fgc/bgc/brdc
        list($fgc, $bgc, $brdc) = array_pad(explode('/',$color), 3, '');
        $margin = ($brdc ? $size+1 : 0); // if border(-color) add margin
    
        $n = $this->getModuleCount();
        $svgSize = $n * $size + 2 * $margin; // square; default: viewBox 0 0 $svgSize $svgSize
        $svgOut = "<svg width=$svgSize height=$svgSize xmlns='http://www.w3.org/2000/svg'>";
        if ($title) $svgOut .= "<title>$title</title>";
    
        // no bgc = 'transparent' background, no brdc = no border
        if ($bgc || $brdc) { // background or border color
            if (!$bgc) $bgc = 'transparent';
            $attr = "fill='$bgc'" . ($brdc ? " stroke='$brdc' stroke-width=1" : '');
            $svgOut .= "<rect x=0 y=0 width=$svgSize height=$svgSize $attr />";
        }
    
        // use path to draw hor.line sections; default: stroke-linecap="butt"
        $svgOut .= "<path fill='none' stroke='$fgc' stroke-width=$size d='";
        for ($r = 0; $r < $n; $r++) {
            $y = (int)floor(($r + 0.5) * $size) + $margin; // half-way rows!
            $svgOut .= "M$margin $y"; // newline (abs.move)
            $currX = $lastX = $margin;
            for ($c = 0; $c < $n; $c++) {
                $new = $this->isDark($r,$c);
                if ($c==0) $curr = $new; // first cell
                elseif ($new!==$curr) { // dark <-> notDark
                    $x = $currX - $lastX; // relative distance
                    if ($curr) $svgOut .= "h$x"; // draw horz.
                    else $svgOut .= "m$x 0"; // move horz.
                    $curr = $new;
                    $lastX = $currX;
                }
                $currX += $size;
            }
            if ($curr) { // last cell
                $x = $currX - $lastX;
                $svgOut .= "h$x"; // draw until eol
            }
        }
        $svgOut .= "'/></svg>";
        return $svgOut;
    }

NB. a 2-color PNG is still the smallest QRcode to publish

@singodiyashubham87
Copy link

Damn! You guys did a great work. @maxpelic @RIMhosting
Would be great if it had been included.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants