Passed
Push — main ( 8c75d8...000860 )
by smiley
10:08
created

QRMarkup::html()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 5
eloc 15
nc 12
nop 1
dl 0
loc 28
rs 9.4555
c 2
b 0
f 1
1
<?php
2
/**
3
 * Class QRMarkup
4
 *
5
 * @created      17.12.2016
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2016 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\QRCode\Output;
12
13
use chillerlan\QRCode\Data\QRMatrix;
14
use chillerlan\QRCode\QRCode;
15
16
use function implode, is_string, sprintf, strip_tags, trim;
17
18
/**
19
 * Converts the matrix into markup types: HTML, SVG, ...
20
 */
21
class QRMarkup extends QROutputAbstract{
22
23
	/**
24
	 * @inheritDoc
25
	 */
26
	protected function moduleValueIsValid($value):bool{
27
		return is_string($value);
28
	}
29
30
	/**
31
	 * @inheritDoc
32
	 */
33
	protected function getModuleValue($value):string{
34
		return trim(strip_tags($value), " '\"\r\n\t");
35
	}
36
37
	/**
38
	 * @inheritDoc
39
	 */
40
	protected function getDefaultModuleValue(bool $isDark):string{
41
		return $isDark ? $this->options->markupDark : $this->options->markupLight;
42
	}
43
44
	/**
45
	 * @inheritDoc
46
	 */
47
	public function dump(string $file = null){
48
		$file       ??= $this->options->cachefile;
49
		$saveToFile   = $file !== null;
50
51
		switch($this->options->outputType){
52
			case QRCode::OUTPUT_MARKUP_HTML:
53
				$data = $this->html($saveToFile);
54
				break;
55
			case QRCode::OUTPUT_MARKUP_SVG:
56
			default:
57
				$data = $this->svg($saveToFile);
58
		}
59
60
		if($saveToFile){
61
			$this->saveToFile($data, $file);
62
		}
63
64
		return $data;
65
	}
66
67
	/**
68
	 * HTML output
69
	 */
70
	protected function html(bool $saveToFile):string{
71
72
		$html = empty($this->options->cssClass)
73
			? '<div>'
74
			: sprintf('<div class="%s">', $this->options->cssClass);
75
76
		$html .= $this->options->eol;
77
78
		foreach($this->matrix->matrix() as $row){
79
			$html .= '<div>';
80
81
			foreach($row as $M_TYPE){
82
				$html .= sprintf('<span style="background: %s;"></span>', $this->moduleValues[$M_TYPE]);
83
			}
84
85
			$html .= '</div>'.$this->options->eol;
86
		}
87
88
		$html .= '</div>'.$this->options->eol;
89
90
		if($saveToFile){
91
			return sprintf(
92
				'<!DOCTYPE html><head><meta charset="UTF-8"><title>QR Code</title></head><body>%s</body>',
93
				$this->options->eol.$html
94
			);
95
		}
96
97
		return $html;
98
	}
99
100
	/**
101
	 * SVG output
102
	 *
103
	 * @see https://github.com/codemasher/php-qrcode/pull/5
104
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
105
	 * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
106
	 */
107
	protected function svg(bool $saveToFile):string{
108
		$svg = $this->svgHeader();
109
110
		if(!empty($this->options->svgDefs)){
111
			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
112
		}
113
114
		$svg .= $this->svgPaths();
115
116
		// close svg
117
		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
118
119
		// transform to data URI only when not saving to file
120
		if(!$saveToFile && $this->options->imageBase64){
121
			$svg = $this->base64encode($svg, 'image/svg+xml');
122
		}
123
124
		return $svg;
125
	}
126
127
	/**
128
	 * returns the <svg> header with the given options parsed
129
	 */
130
	protected function svgHeader():string{
131
		$width  = $this->options->svgWidth !== null ? sprintf(' width="%s"', $this->options->svgWidth) : '';
132
		$height = $this->options->svgHeight !== null ? sprintf(' height="%s"', $this->options->svgHeight) : '';
133
134
		/** @noinspection HtmlUnknownAttribute */
135
		return sprintf(
136
			'<?xml version="1.0" encoding="UTF-8"?>%6$s'.
137
			'<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',
138
			$this->options->cssClass,
139
			$this->options->svgViewBoxSize ?? $this->moduleCount,
140
			$this->options->svgPreserveAspectRatio,
141
			$width,
142
			$height,
143
			$this->options->eol
144
		);
145
	}
146
147
	/**
148
	 * returns one or more SVG <path> elements
149
	 *
150
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
151
	 */
152
	protected function svgPaths():string{
153
		$paths = $this->collectModules(fn(int $x, int $y):string => $this->svgModule($x, $y));
154
		$svg   = [];
155
156
		// create the path elements
157
		foreach($paths as $M_TYPE => $path){
158
			$path = trim(implode(' ', $path));
159
160
			if(empty($path)){
161
				continue;
162
			}
163
164
			$cssClass = implode(' ', [
165
				'qr-'.$M_TYPE,
166
				($M_TYPE & QRMatrix::IS_DARK) === QRMatrix::IS_DARK ? 'dark' : 'light',
167
				$this->options->cssClass,
168
			]);
169
170
			$format = empty($this->moduleValues[$M_TYPE])
171
				? '<path class="%1$s" d="%2$s"/>'
172
				: '<path class="%1$s" fill="%3$s" fill-opacity="%4$s" d="%2$s"/>';
173
174
			$svg[] = sprintf($format, $cssClass, $path, $this->moduleValues[$M_TYPE], $this->options->svgOpacity);
175
		}
176
177
		return implode($this->options->eol, $svg);
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

177
		return implode(/** @scrutinizer ignore-type */ $this->options->eol, $svg);
Loading history...
178
	}
179
180
	/**
181
	 * returns a path segment for a single module
182
	 *
183
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
184
	 */
185
	protected function svgModule(int $x, int $y):string{
186
187
		if($this->options->imageTransparent && !$this->matrix->check($x, $y)){
188
			return '';
189
		}
190
191
		if($this->options->drawCircularModules && !$this->matrix->checkTypes($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::checkTypes() 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

191
		if($this->options->drawCircularModules && !$this->matrix->checkTypes($x, $y, /** @scrutinizer ignore-type */ $this->options->keepAsSquare)){
Loading history...
192
			$r = $this->options->circleRadius;
193
194
			return sprintf(
195
				'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',
196
				($x + 0.5 - $r),
197
				($y + 0.5),
198
				$r,
199
				($r * 2)
200
			);
201
202
		}
203
204
		return sprintf('M%1$s %2$s h%3$s v1 h-%4$sZ', $x, $y, 1, 1);
205
	}
206
207
}
208