diff --git a/examples/svg.php b/examples/svg.php index 3c837e324..f8549f243 100644 --- a/examples/svg.php +++ b/examples/svg.php @@ -17,39 +17,51 @@ require_once __DIR__.'/../vendor/autoload.php'; $data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s'; $options = new QROptions([ - 'version' => 5, + 'version' => 20, 'outputType' => QRCode::OUTPUT_MARKUP_SVG, 'eccLevel' => QRCode::ECC_L, - 'scale' => 10, + 'scale' => 5, + 'addQuietzone' => true, + 'svgOpacity' => 0.8, + 'svgDefs' => ' + + + + + + + + + ', 'moduleValues' => [ // finder - 1536 => '#A71111', // dark (true) - 6 => '#FFBFBF', // light (false) + 1536 => 'url(#g1)', // dark (true) + 6 => '#eee', // light (false) // alignment - 2560 => '#A70364', - 10 => '#FFC9C9', + 2560 => 'url(#g1)', + 10 => '#eee', // timing - 3072 => '#98005D', - 12 => '#FFB8E9', + 3072 => 'url(#g1)', + 12 => '#eee', // format - 3584 => '#003804', - 14 => '#00FB12', + 3584 => 'url(#g1)', + 14 => '#eee', // version - 4096 => '#650098', - 16 => '#E0B8FF', + 4096 => 'url(#g1)', + 16 => '#eee', // data - 1024 => '#4A6000', - 4 => '#ECF9BE', + 1024 => 'url(#g2)', + 4 => '#eee', // darkmodule - 512 => '#080063', + 512 => 'url(#g1)', // separator - 8 => '#AFBFBF', + 8 => '#eee', // quietzone - 18 => '#FFFFFF', + 18 => '#eee', ], ]); -header('Content-type: image/svg+xml'); +#header('Content-type: image/svg+xml'); echo (new QRCode($options))->render($data); diff --git a/src/Output/QRMarkup.php b/src/Output/QRMarkup.php index 7b191310a..425a1acdd 100644 --- a/src/Output/QRMarkup.php +++ b/src/Output/QRMarkup.php @@ -21,6 +21,7 @@ class QRMarkup extends QROutputAbstract{ /** * @return string + * @throws \chillerlan\QRCode\Output\QRCodeOutputException */ public function dump(){ @@ -68,52 +69,60 @@ class QRMarkup extends QROutputAbstract{ * @return string|bool */ protected function toSVG(){ - $length = ($this->moduleCount + ($this->options->addQuietzone ? 8 : 0)) * $this->options->scale; + $scale = $this->options->scale; + $length = $this->moduleCount * $scale; + $matrix = $this->matrix->matrix(); - // svg header - $svg = ''.$this->options->eol. - ''.$this->options->eol; + $svg = '' + .$this->options->eol + .''.$this->options->svgDefs.'' + .$this->options->eol; - // @todo: optimize -> see https://github.com/alexeyten/qr-image/blob/master/lib/vector.js - foreach($this->options->moduleValues as $key => $value){ + foreach($this->options->moduleValues as $M_TYPE => $value){ // fallback if(is_bool($value)){ $value = $value ? '#000' : '#fff'; } - // svg body - foreach($this->matrix->matrix() as $y => $row){ + $path = ''; + + foreach($matrix as $y => $row){ //we'll combine active blocks within a single row as a lightweight compression technique - $from = -1; + $start = null; $count = 0; - foreach($row as $x => $pixel){ - if($pixel === $key){ + foreach($row as $x => $module){ + + if($module === $M_TYPE){ $count++; - if($from < 0){ - $from = $x; + if($start === null){ + $start = $x * $scale; + } + + if($row[$x + 1] ?? false){ + continue; } } - elseif($from >= 0){ - $svg .= 'options->cssClass) !== '' ? ' class="'.$this->options->cssClass.'"' :'').' />' - .$this->options->eol; + + if($count > 0){ + $len = $count * $scale; + $path .= 'M' .$start. ' ' .($y * $scale). ' h'.$len.' v'.$scale.' h-'.$len.'Z '; // reset count - $from = -1; $count = 0; + $start = null; } + } - // close off the row, if applicable - if($from >= 0){ - $svg .= ''.$this->options->eol; - } } + + if(!empty($path)){ + $svg .= ''; + } + } // close svg diff --git a/src/QROptions.php b/src/QROptions.php index 85693116c..6da9ebe93 100644 --- a/src/QROptions.php +++ b/src/QROptions.php @@ -32,6 +32,8 @@ use chillerlan\Traits\Container; * @property int $scale * * @property string $cssClass + * @property string $svgOpacity + * @property string $svgDefs * * @property string $textDark * @property string $textLight @@ -154,6 +156,22 @@ class QROptions{ */ protected $cssClass; + /** + * SVG opacity + * + * @var float + */ + protected $svgOpacity = 1.0; + + /** + * anything between + * + * @see https://developer.mozilla.org/docs/Web/SVG/Element/defs + * + * @var string + */ + protected $svgDefs = ''; + /** * string substitute for dark *