kazuhikoarase / qrcode-generator

QR Code Generator implementation in JavaScript, Java and more.
https://kazuhikoarase.github.io/qrcode-generator/js/demo/
MIT License
2.13k stars 683 forks source link

Compress QR code #102

Open maxpelic opened 3 years ago

maxpelic commented 3 years ago

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 commented 3 years ago

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 commented 2 years ago

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 commented 1 year ago

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