Passed
Push — main ( 547c31...278554 )
by smiley
02:11
created

QROptionsTrait::set_eccLevel()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Trait QROptionsTrait
4
 *
5
 * @created      10.03.2018
6
 * @author       smiley <[email protected]>
7
 * @copyright    2018 smiley
8
 * @license      MIT
9
 *
10
 * @noinspection PhpUnused
11
 */
12
13
namespace chillerlan\QRCode;
14
15
use chillerlan\QRCode\Common\EccLevel;
16
17
use chillerlan\QRCode\Decoder\{GDLuminanceSource, IMagickLuminanceSource};
18
use function array_values, count, extension_loaded, in_array, is_numeric, max, min, sprintf, strtolower;
19
20
/**
21
 * The QRCode plug-in settings & setter functionality
22
 */
23
trait QROptionsTrait{
24
25
	/**
26
	 * QR Code version number
27
	 *
28
	 * [1 ... 40] or QRCode::VERSION_AUTO
29
	 */
30
	protected int $version = QRCode::VERSION_AUTO;
31
32
	/**
33
	 * Minimum QR version
34
	 *
35
	 * if $version = QRCode::VERSION_AUTO
36
	 */
37
	protected int $versionMin = 1;
38
39
	/**
40
	 * Maximum QR version
41
	 */
42
	protected int $versionMax = 40;
43
44
	/**
45
	 * Error correct level
46
	 *
47
	 * QRCode::ECC_X where X is:
48
	 *
49
	 *   - L =>  7%
50
	 *   - M => 15%
51
	 *   - Q => 25%
52
	 *   - H => 30%
53
	 */
54
	protected int $eccLevel = EccLevel::L;
55
56
	/**
57
	 * Mask Pattern to use (no value in using, mostly for unit testing purposes)
58
	 *
59
	 * [0...7] or QRCode::MASK_PATTERN_AUTO
60
	 */
61
	protected int $maskPattern = QRCode::MASK_PATTERN_AUTO;
62
63
	/**
64
	 * Add a "quiet zone" (margin) according to the QR code spec
65
	 */
66
	protected bool $addQuietzone = true;
67
68
	/**
69
	 * Size of the quiet zone
70
	 *
71
	 * internally clamped to [0 ... $moduleCount / 2], defaults to 4 modules
72
	 */
73
	protected int $quietzoneSize = 4;
74
75
	/**
76
	 * The output type
77
	 *
78
	 *   - QRCode::OUTPUT_MARKUP_XXXX where XXXX = HTML, SVG
79
	 *   - QRCode::OUTPUT_IMAGE_XXX where XXX = PNG, GIF, JPG
80
	 *   - QRCode::OUTPUT_STRING_XXXX where XXXX = TEXT, JSON
81
	 *   - QRCode::OUTPUT_CUSTOM
82
	 */
83
	protected string $outputType = QRCode::OUTPUT_MARKUP_SVG;
84
85
	/**
86
	 * the FQCN of the custom QROutputInterface if $outputType is set to QRCode::OUTPUT_CUSTOM
87
	 */
88
	protected ?string $outputInterface = null;
89
90
	/**
91
	 * /path/to/cache.file
92
	 */
93
	protected ?string $cachefile = null;
94
95
	/**
96
	 * newline string [HTML, SVG, TEXT]
97
	 */
98
	protected string $eol = PHP_EOL;
99
100
	/**
101
	 * size of a QR code pixel [SVG, IMAGE_*], HTML via CSS
102
	 */
103
	protected int $scale = 5;
104
105
	/**
106
	 * a common css class
107
	 */
108
	protected string $cssClass = 'qrcode';
109
110
	/**
111
	 * SVG opacity
112
	 */
113
	protected float $svgOpacity = 1.0;
114
115
	/**
116
	 * anything between <defs>
117
	 *
118
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
119
	 */
120
	protected string $svgDefs = '';
121
122
	/**
123
	 * SVG viewBox size. a single integer number which defines width/height of the viewBox attribute.
124
	 *
125
	 * viewBox="0 0 x x"
126
	 *
127
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox
128
	 * @see https://css-tricks.com/scale-svg/#article-header-id-3
129
	 */
130
	protected ?int $svgViewBoxSize = null;
131
132
	/**
133
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio
134
	 */
135
	protected string $svgPreserveAspectRatio = 'xMidYMid';
136
137
	/**
138
	 * optional "width" attribute with the specified value (note that the value is not checked!)
139
	 *
140
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/width
141
	 */
142
	protected ?string $svgWidth = null;
143
144
	/**
145
	 * optional "height" attribute with the specified value (note that the value is not checked!)
146
	 *
147
	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/height
148
	 */
149
	protected ?string $svgHeight = null;
150
151
	/**
152
	 * whether to connect the paths for the several module types to avoid weird glitches when using gradients etc.
153
	 *
154
	 * @see https://github.com/chillerlan/php-qrcode/issues/57
155
	 */
156
	protected bool $svgConnectPaths = false;
157
158
	/**
159
	 * specify which paths/patterns to exclude from connecting if $svgConnectPaths is set to true
160
	 */
161
	protected array $svgExcludeFromConnect = [];
162
163
	/**
164
	 * specify whether to draw the modules as filled circles
165
	 */
166
	protected bool $svgDrawCircularModules = false;
167
168
	/**
169
	 * specifies the radius of the modules when $svgDrawCircularModules is set to true
170
	 */
171
	protected float $svgCircleRadius = 0.45;
172
173
	/**
174
	 * specifies which module types to exclude when $svgDrawCircularModules is set to true
175
	 */
176
	protected array $svgKeepAsSquare = [];
177
178
	/**
179
	 * string substitute for dark
180
	 */
181
	protected string $textDark = '🔴';
182
183
	/**
184
	 * string substitute for light
185
	 */
186
	protected string $textLight = '⭕';
187
188
	/**
189
	 * markup substitute for dark (CSS value)
190
	 */
191
	protected string $markupDark = '#000';
192
193
	/**
194
	 * markup substitute for light (CSS value)
195
	 */
196
	protected string $markupLight = '#fff';
197
198
	/**
199
	 * Return the image resource instead of a render if applicable.
200
	 * This option overrides other output options, such as $cachefile and $imageBase64.
201
	 *
202
	 * Supported by the following modules:
203
	 *
204
	 * - QRImage:   resource (PHP < 8), GdImage
205
	 * - QRImagick: Imagick
206
	 * - QRFpdf:    FPDF
207
	 *
208
	 * @see \chillerlan\QRCode\Output\QROutputInterface::dump()
209
	 *
210
	 * @var bool
211
	 */
212
	protected bool $returnResource = false;
213
214
	/**
215
	 * toggle base64 or raw image data
216
	 */
217
	protected bool $imageBase64 = true;
218
219
	/**
220
	 * toggle transparency, not supported by jpg
221
	 */
222
	protected bool $imageTransparent = true;
223
224
	/**
225
	 * @see imagecolortransparent()
226
	 *
227
	 * [R, G, B]
228
	 */
229
	protected array $imageTransparencyBG = [255, 255, 255];
230
231
	/**
232
	 * @see imagepng()
233
	 */
234
	protected int $pngCompression = -1;
235
236
	/**
237
	 * @see imagejpeg()
238
	 */
239
	protected int $jpegQuality = 85;
240
241
	/**
242
	 * Imagick output format
243
	 *
244
	 * @see \Imagick::setType()
245
	 */
246
	protected string $imagickFormat = 'png';
247
248
	/**
249
	 * Imagick background color (defaults to "transparent")
250
	 *
251
	 * @see \ImagickPixel::__construct()
252
	 */
253
	protected ?string $imagickBG = null;
254
255
	/**
256
	 * Measurement unit for FPDF output: pt, mm, cm, in (defaults to "pt")
257
	 *
258
	 * @see \FPDF::__construct()
259
	 */
260
	protected string $fpdfMeasureUnit = 'pt';
261
262
	/**
263
	 * Module values map
264
	 *
265
	 *   - HTML, IMAGICK: #ABCDEF, cssname, rgb(), rgba()...
266
	 *   - IMAGE: [63, 127, 255] // R, G, B
267
	 */
268
	protected ?array $moduleValues = null;
269
270
	/**
271
	 * use Imaagick (if available) when reading QR Codes
272
	 */
273
	protected bool $readerUseImagickIfAvailable = false;
274
275
	/**
276
	 * grayscale the image before reading
277
	 */
278
	protected bool $readerGrayscale = false;
279
280
	/**
281
	 * increase the contrast before reading
282
	 *
283
	 * note that applying contrast works different in GD and Imagick, so mileage may vary
284
	 */
285
	protected bool $readerIncreaseContrast = false;
286
287
	/**
288
	 * Toggles logo space creation
289
	 */
290
	protected bool $addLogoSpace = false;
291
292
	/**
293
	 * width of the logo space
294
	 */
295
	protected int $logoSpaceWidth = 0;
296
297
	/**
298
	 * height of the logo space
299
	 */
300
	protected int $logoSpaceHeight = 0;
301
302
	/**
303
	 * optional horizontal start position of the logo space (top left corner)
304
	 */
305
	protected ?int $logoSpaceStartX = null;
306
307
	/**
308
	 * optional vertical start position of the logo space (top left corner)
309
	 */
310
	protected ?int $logoSpaceStartY = null;
311
312
	/**
313
	 * clamp min/max version number
314
	 */
315
	protected function setMinMaxVersion(int $versionMin, int $versionMax):void{
316
		$min = max(1, min(40, $versionMin));
317
		$max = max(1, min(40, $versionMax));
318
319
		$this->versionMin = min($min, $max);
320
		$this->versionMax = max($min, $max);
321
	}
322
323
	/**
324
	 * sets the minimum version number
325
	 */
326
	protected function set_versionMin(int $version):void{
327
		$this->setMinMaxVersion($version, $this->versionMax);
328
	}
329
330
	/**
331
	 * sets the maximum version number
332
	 */
333
	protected function set_versionMax(int $version):void{
334
		$this->setMinMaxVersion($this->versionMin, $version);
335
	}
336
337
	/**
338
	 * sets the error correction level
339
	 *
340
	 * @throws \chillerlan\QRCode\QRCodeException
341
	 */
342
	protected function set_eccLevel(int $eccLevel):void{
343
344
		if(!in_array($eccLevel, [EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H], true)){
345
			throw new QRCodeException(sprintf('Invalid error correct level: %s', $eccLevel));
346
		}
347
348
		$this->eccLevel = $eccLevel;
349
	}
350
351
	/**
352
	 * sets/clamps the mask pattern
353
	 */
354
	protected function set_maskPattern(int $maskPattern):void{
355
356
		if($maskPattern !== QRCode::MASK_PATTERN_AUTO){
357
			$this->maskPattern = max(0, min(7, $maskPattern));
358
		}
359
360
	}
361
362
	/**
363
	 * sets the transparency background color
364
	 *
365
	 * @throws \chillerlan\QRCode\QRCodeException
366
	 */
367
	protected function set_imageTransparencyBG(array $imageTransparencyBG):void{
368
369
		// invalid value - set to white as default
370
		if(count($imageTransparencyBG) < 3){
371
			$this->imageTransparencyBG = [255, 255, 255];
372
373
			return;
374
		}
375
376
		foreach($imageTransparencyBG as $k => $v){
377
378
			// cut off exceeding items
379
			if($k > 2){
380
				break;
381
			}
382
383
			if(!is_numeric($v)){
384
				throw new QRCodeException('Invalid RGB value.');
385
			}
386
387
			// clamp the values
388
			$this->imageTransparencyBG[$k] = max(0, min(255, (int)$v));
389
		}
390
391
		// use the array values to not run into errors with the spread operator (...$arr)
392
		$this->imageTransparencyBG = array_values($this->imageTransparencyBG);
393
	}
394
395
	/**
396
	 * sets/clamps the version number
397
	 */
398
	protected function set_version(int $version):void{
399
400
		if($version !== QRCode::VERSION_AUTO){
401
			$this->version = max(1, min(40, $version));
402
		}
403
404
	}
405
406
	/**
407
	 * sets the FPDF measurement unit
408
	 *
409
	 * @codeCoverageIgnore
410
	 */
411
	protected function set_fpdfMeasureUnit(string $unit):void{
412
		$unit = strtolower($unit);
413
414
		if(in_array($unit, ['cm', 'in', 'mm', 'pt'], true)){
415
			$this->fpdfMeasureUnit = $unit;
416
		}
417
418
		// @todo throw or ignore silently?
419
	}
420
421
	/**
422
	 * enables Imagick for the QR Code reader if the extension is available
423
	 */
424
	protected function set_readerUseImagickIfAvailable(bool $useImagickIfAvailable):void{
425
		$this->readerUseImagickIfAvailable = $useImagickIfAvailable && extension_loaded('imagick');
426
	}
427
428
	/**
429
	 * returns the FQCN of the luminance source class to use in the reader (GD or Imagick)
430
	 *
431
	 * @see \chillerlan\QRCode\Decoder\LuminanceSourceInterface
432
	 */
433
	public function getLuminanceSourceFQCN():string{
434
		// i still hate this
435
		return $this->readerUseImagickIfAvailable
436
			? IMagickLuminanceSource::class
437
			: GDLuminanceSource::class;
438
	}
439
440
	/**
441
	 * clamp the logo space values between 0 and maximum length (177 modules at version 40)
442
	 */
443
	protected function clampLogoSpaceValue(int $value):int{
444
		return (int)max(0, min(177, $value));
445
	}
446
447
	/**
448
	 * clamp/set logo space width
449
	 */
450
	protected function set_logoSpaceWidth(int $value):void{
451
		$this->logoSpaceWidth = $this->clampLogoSpaceValue($value);
452
	}
453
454
	/**
455
	 * clamp/set logo space height
456
	 */
457
	protected function set_logoSpaceHeight(int $value):void{
458
		$this->logoSpaceHeight = $this->clampLogoSpaceValue($value);
459
	}
460
461
	/**
462
	 * clamp/set horizontal logo space start
463
	 */
464
	protected function set_logoSpaceStartX(?int $value):void{
465
		$this->logoSpaceStartX = $value === null ? null : $this->clampLogoSpaceValue($value);
466
	}
467
468
	/**
469
	 * clamp/set vertical logo space start
470
	 */
471
	protected function set_logoSpaceStartY(?int $value):void{
472
		$this->logoSpaceStartY = $value === null ? null : $this->clampLogoSpaceValue($value);
473
	}
474
475
}
476