Passed
Push — main ( ccad9c...423454 )
by smiley
11:28
created

QROptionsTrait::set_maskPattern()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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