better SVG output

This commit is contained in:
smiley
2017-12-29 06:17:21 +01:00
parent 0929aa01b4
commit 465351ca86
3 changed files with 81 additions and 42 deletions
+30 -18
View File
@@ -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' => '
<linearGradient id="g2">
<stop offset="0%" stop-color="#39F" />
<stop offset="100%" stop-color="#F3F" />
</linearGradient>
<linearGradient id="g1">
<stop offset="0%" stop-color="#F3F" />
<stop offset="100%" stop-color="#39F" />
</linearGradient>
<style>rect{shape-rendering:crispEdges}</style>',
'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);
+33 -24
View File
@@ -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 = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'.$length.'" height="'.$length.'" viewBox="0 0 '.$length.' '.$length.'">'.$this->options->eol.
'<defs><style>rect{shape-rendering:crispEdges}</style></defs>'.$this->options->eol;
$svg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'.$length.'px" height="'.$length.'px">'
.$this->options->eol
.'<defs>'.$this->options->svgDefs.'</defs>'
.$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 .= '<rect x="'.($from * $this->options->scale).'" y="'.($y * $this->options->scale)
.'" width="'.($this->options->scale * $count).'" height="'.$this->options->scale.'" fill="'.$value.'"'
.(trim($this->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 .= '<rect x="'.($from * $this->options->scale).'" y="'.($y * $this->options->scale)
.'" width="'.($this->options->scale * $count).'" height="'.$this->options->scale.'" class="'.$this->options->cssClass.'" fill="'.$value.'" />'.$this->options->eol;
}
}
if(!empty($path)){
$svg .= '<path class="qr-'.$M_TYPE.' '.$this->options->cssClass.'" stroke="transparent" fill="'.$value.'" fill-opacity="'.$this->options->svgOpacity.'" d="'.$path.'" />';
}
}
// close svg
+18
View File
@@ -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 <defs>
*
* @see https://developer.mozilla.org/docs/Web/SVG/Element/defs
*
* @var string
*/
protected $svgDefs = '<style>rect{shape-rendering:crispEdges}</style>';
/**
* string substitute for dark
*