mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-30 12:07:32 +00:00
✨ better SVG output
This commit is contained in:
+30
-18
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user