Passed
Push — main ( 19f4fc...1ca995 )
by smiley
01:50
created

QRMarkupSVG::paths()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 6
eloc 20
c 3
b 0
f 0
nc 11
nop 0
dl 0
loc 35
rs 8.9777
1
<?php
2
/**
3
 * Class QRMarkupSVG
4
 *
5
 * @created      06.06.2022
6
 * @author       smiley <[email protected]>
7
 * @copyright    2022 smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\QRCode\Output;
12
13
use chillerlan\QRCode\Data\QRMatrix;
14
use function array_chunk, implode, sprintf;
15
16
/**
17
 * SVG output
18
 *
19
 * @see https://github.com/codemasher/php-qrcode/pull/5
20
 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
21
 * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
22
 * @see http://apex.infogridpacific.com/SVG/svg-tutorial-contents.html
23
 */
24
class QRMarkupSVG extends QRMarkup{
25
26
	/**
27
	 * @inheritDoc
28
	 */
29
	protected function createMarkup(bool $saveToFile):string{
30
		$svg = $this->header();
31
32
		if(!empty($this->options->svgDefs)){
33
			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
34
		}
35
36
		$svg .= $this->paths();
37
38
		// close svg
39
		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
40
41
		// transform to data URI only when not saving to file
42
		if(!$saveToFile && $this->options->imageBase64){
43
			$svg = $this->base64encode($svg, 'image/svg+xml');
44
		}
45
46
		return $svg;
47
	}
48
49
	/**
50
	 * returns the <svg> header with the given options parsed
51
	 */
52
	protected function header():string{
53
		$width  = $this->options->svgWidth !== null ? sprintf(' width="%s"', $this->options->svgWidth) : '';
54
		$height = $this->options->svgHeight !== null ? sprintf(' height="%s"', $this->options->svgHeight) : '';
55
56
		/** @noinspection HtmlUnknownAttribute */
57
		return sprintf(
58
			'<?xml version="1.0" encoding="UTF-8"?>%6$s'.
59
			'<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" viewBox="0 0 %2$s %2$s" preserveAspectRatio="%3$s"%4$s%5$s>%6$s',
60
			$this->options->cssClass,
61
			$this->options->svgViewBoxSize ?? $this->moduleCount,
62
			$this->options->svgPreserveAspectRatio,
63
			$width,
64
			$height,
65
			$this->options->eol
66
		);
67
	}
68
69
	/**
70
	 * returns one or more SVG <path> elements
71
	 *
72
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
73
	 */
74
	protected function paths():string{
75
		$paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => $this->module($x, $y, $M_TYPE));
76
		$svg   = [];
77
78
		// create the path elements
79
		foreach($paths as $M_TYPE => $modules){
80
			// limit the total line length
81
			$chunks = array_chunk($modules, 100);
82
			$chonks = [];
83
84
			foreach($chunks as $chunk){
85
				$chonks[] = implode(' ', $chunk);
86
			}
87
88
			$path = implode($this->options->eol, $chonks);
0 ignored issues
show
Bug introduced by
It seems like $this->options->eol can also be of type null; however, parameter $glue of implode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
			$path = implode(/** @scrutinizer ignore-type */ $this->options->eol, $chonks);
Loading history...
89
90
			if(empty($path)){
91
				continue;
92
			}
93
94
			// ignore non-existent module values
95
			$format = !isset($this->moduleValues[$M_TYPE]) || empty($this->moduleValues[$M_TYPE])
96
				? '<path class="%1$s" d="%2$s"/>'
97
				: '<path class="%1$s" fill="%3$s" fill-opacity="%4$s" d="%2$s"/>';
98
99
			$svg[] = sprintf(
100
				$format,
101
				$this->getCssClass($M_TYPE),
102
				$path,
103
				$this->moduleValues[$M_TYPE] ?? '',
104
				$this->options->svgOpacity)
105
			;
106
		}
107
108
		return implode($this->options->eol, $svg);
109
	}
110
111
	/**
112
	 * @inheritDoc
113
	 */
114
	protected function getCssClass(int $M_TYPE):string{
115
		return implode(' ', [
116
			'qr-'.$M_TYPE,
117
			($M_TYPE & QRMatrix::IS_DARK) === QRMatrix::IS_DARK ? 'dark' : 'light',
118
			$this->options->cssClass,
119
		]);
120
	}
121
122
	/**
123
	 * returns a path segment for a single module
124
	 *
125
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
126
	 */
127
	protected function module(int $x, int $y, int $M_TYPE):string{
0 ignored issues
show
Unused Code introduced by
The parameter $M_TYPE is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

127
	protected function module(int $x, int $y, /** @scrutinizer ignore-unused */ int $M_TYPE):string{

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
128
129
		if($this->options->imageTransparent && !$this->matrix->check($x, $y)){
130
			return '';
131
		}
132
133
		if($this->options->drawCircularModules && $this->matrix->checkTypeNotIn($x, $y, $this->options->keepAsSquare)){
0 ignored issues
show
Bug introduced by
It seems like $this->options->keepAsSquare can also be of type null; however, parameter $M_TYPES of chillerlan\QRCode\Data\QRMatrix::checkTypeNotIn() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

133
		if($this->options->drawCircularModules && $this->matrix->checkTypeNotIn($x, $y, /** @scrutinizer ignore-type */ $this->options->keepAsSquare)){
Loading history...
134
			$r = $this->options->circleRadius;
135
136
			return sprintf(
137
				'M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z',
138
				($x + 0.5 - $r),
139
				($y + 0.5),
140
				$r,
141
				($r * 2)
142
			);
143
144
		}
145
146
		return sprintf('M%1$s %2$s h1 v1 h-1Z', $x, $y);
147
	}
148
149
}
150