PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.8
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.8
28.6 28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 All 130 releases
wordpress-seo / vendor_prefixed / chillerlan / php-qrcode / src / Data / QRMatrix.php

QRMatrix.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.8, at vendor_prefixed/chillerlan/php-qrcode/src/Data/QRMatrix.php

500 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class QRMatrix
5 *
6 * @filesource QRMatrix.php
7 * @created 15.11.2017
8 * @package chillerlan\QRCode\Data
9 * @author Smiley <[email protected]>
10 * @copyright 2017 Smiley
11 * @license MIT
12 */
13 namespace YoastSEO_Vendor\chillerlan\QRCode\Data;
14
15 use YoastSEO_Vendor\chillerlan\QRCode\QRCode;
16 /**
17 * @link http://www.thonky.com/qr-code-tutorial/format-version-information
18 */
19 class QRMatrix
20 {
21 const M_NULL = 0x0;
22 const M_DARKMODULE = 0x2;
23 const M_DATA = 0x4;
24 const M_FINDER = 0x6;
25 const M_SEPARATOR = 0x8;
26 const M_ALIGNMENT = 0xa;
27 const M_TIMING = 0xc;
28 const M_FORMAT = 0xe;
29 const M_VERSION = 0x10;
30 const M_QUIETZONE = 0x12;
31 const M_LOGO = 0x14;
32 // @todo
33 const M_TEST = 0xff;
34 /**
35 * @link http://www.thonky.com/qr-code-tutorial/alignment-pattern-locations
36 */
37 const alignmentPattern = [1 => [], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]];
38 /**
39 * @link http://www.thonky.com/qr-code-tutorial/format-version-tables
40 */
41 const versionPattern = [
42 7 => 0x7c94,
43 0x85bc,
44 0x9a99,
45 0xa4d3,
46 // 7-10
47 0xbbf6,
48 0xc762,
49 0xd847,
50 0xe60d,
51 0xf928,
52 0x10b78,
53 0x1145d,
54 0x12a17,
55 0x13532,
56 0x149a6,
57 0x15683,
58 0x168c9,
59 0x177ec,
60 0x18ec4,
61 0x191e1,
62 0x1afab,
63 0x1b08e,
64 0x1cc1a,
65 0x1d33f,
66 0x1ed75,
67 0x1f250,
68 0x209d5,
69 0x216f0,
70 0x228ba,
71 0x2379f,
72 0x24b0b,
73 0x2542e,
74 0x26a64,
75 0x27541,
76 0x28c69,
77 ];
78 const formatPattern = [
79 [0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976],
80 // L
81 [0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0],
82 // M
83 [0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed],
84 // Q
85 [0x1689, 0x13be, 0x1ce7, 0x19d0, 0x762, 0x255, 0xd0c, 0x83b],
86 ];
87 /**
88 * @var int
89 */
90 protected $version;
91 /**
92 * @var int
93 */
94 protected $eclevel;
95 /**
96 * @var int
97 */
98 protected $maskPattern = -1;
99 /**
100 * @var int
101 */
102 protected $moduleCount;
103 /**
104 * @var mixed[]
105 */
106 protected $matrix;
107 /**
108 * QRMatrix constructor.
109 *
110 * @param int $version
111 * @param int $eclevel
112 *
113 * @throws \chillerlan\QRCode\Data\QRCodeDataException
114 */
115 public function __construct($version, $eclevel)
116 {
117 if (!\in_array($version, \range(1, 40), \true)) {
118 throw new \YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException('invalid QR Code version');
119 }
120 if (!\array_key_exists($eclevel, \YoastSEO_Vendor\chillerlan\QRCode\QRCode::ECC_MODES)) {
121 throw new \YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException('invalid ecc level');
122 }
123 $this->version = $version;
124 $this->eclevel = $eclevel;
125 $this->moduleCount = $this->version * 4 + 17;
126 $this->matrix = \array_fill(0, $this->moduleCount, \array_fill(0, $this->moduleCount, $this::M_NULL));
127 }
128 /**
129 * Returns the data matrix, returns a pure boolean representation if $boolean is set to true
130 *
131 * @param bool $boolean
132 *
133 * @return int[][]|bool[][]
134 */
135 public function matrix($boolean = \false)
136 {
137 if (!$boolean) {
138 return $this->matrix;
139 }
140 $matrix = [];
141 foreach ($this->matrix as $y => $row) {
142 $matrix[$y] = [];
143 foreach ($row as $x => $val) {
144 $matrix[$y][$x] = $val >> 8 > 0;
145 }
146 }
147 return $matrix;
148 }
149 /**
150 * @return int
151 */
152 public function version()
153 {
154 return $this->version;
155 }
156 /**
157 * @return int
158 */
159 public function eccLevel()
160 {
161 return $this->eclevel;
162 }
163 /**
164 * @return int
165 */
166 public function maskPattern()
167 {
168 return $this->maskPattern;
169 }
170 /**
171 * Returns the absoulute size of the matrix, including quiet zone (after setting it).
172 *
173 * size = version * 4 + 17 [ + 2 * quietzone size]
174 *
175 * @return int
176 */
177 public function size()
178 {
179 return $this->moduleCount;
180 }
181 /**
182 * Returns the value of the module at position [$x, $y]
183 *
184 * @param int $x
185 * @param int $y
186 *
187 * @return int
188 */
189 public function get($x, $y)
190 {
191 return $this->matrix[$y][$x];
192 }
193 /**
194 * Sets the $M_TYPE value for the module at position [$x, $y]
195 *
196 * true => $M_TYPE << 8
197 * false => $M_TYPE
198 *
199 * @param int $x
200 * @param int $y
201 * @param int $M_TYPE
202 * @param bool $value
203 *
204 * @return \chillerlan\QRCode\Data\QRMatrix
205 */
206 public function set($x, $y, $value, $M_TYPE)
207 {
208 $this->matrix[$y][$x] = $M_TYPE << ($value ? 8 : 0);
209 return $this;
210 }
211 /**
212 * Checks whether a module is true (dark) or false (light)
213 *
214 * true => $value >> 8 === $M_TYPE
215 * $value >> 8 > 0
216 *
217 * false => $value === $M_TYPE
218 * $value >> 8 === 0
219 *
220 * @param int $x
221 * @param int $y
222 *
223 * @return bool
224 */
225 public function check($x, $y)
226 {
227 return $this->matrix[$y][$x] >> 8 > 0;
228 }
229 /**
230 * Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
231 *
232 * @return \chillerlan\QRCode\Data\QRMatrix
233 */
234 public function setDarkModule()
235 {
236 $this->set(8, 4 * $this->version + 9, \true, $this::M_DARKMODULE);
237 return $this;
238 }
239 /**
240 * Draws the 7x7 finder patterns in the corners top left/right and bottom left
241 *
242 * @return \chillerlan\QRCode\Data\QRMatrix
243 */
244 public function setFinderPattern()
245 {
246 $pos = [
247 [0, 0],
248 // top left
249 [$this->moduleCount - 7, 0],
250 // bottom left
251 [0, $this->moduleCount - 7],
252 ];
253 foreach ($pos as $c) {
254 for ($y = 0; $y < 7; $y++) {
255 for ($x = 0; $x < 7; $x++) {
256 $this->set($c[0] + $y, $c[1] + $x, !($x > 0 && $x < 6 && ($y === 1 || $y === 5) || $y > 0 && $y < 6 && ($x === 1 || $x === 5)), $this::M_FINDER);
257 }
258 }
259 }
260 return $this;
261 }
262 /**
263 * Draws the separator lines around the finder patterns
264 *
265 * @return \chillerlan\QRCode\Data\QRMatrix
266 */
267 public function setSeparators()
268 {
269 $h = [[7, 0], [$this->moduleCount - 8, 0], [7, $this->moduleCount - 8]];
270 $v = [[7, 7], [$this->moduleCount - 1, 7], [7, $this->moduleCount - 8]];
271 $t = $this::M_SEPARATOR;
272 for ($c = 0; $c < 3; $c++) {
273 for ($i = 0; $i < 8; $i++) {
274 $this->set($h[$c][0], $h[$c][1] + $i, \false, $t);
275 $this->set($v[$c][0] - $i, $v[$c][1], \false, $t);
276 }
277 }
278 return $this;
279 }
280 /**
281 * Draws the 5x5 alignment patterns
282 *
283 * @return \chillerlan\QRCode\Data\QRMatrix
284 */
285 public function setAlignmentPattern()
286 {
287 $pattern = $this::alignmentPattern[$this->version];
288 foreach ($pattern as $y) {
289 foreach ($pattern as $x) {
290 // skip existing patterns
291 if ($this->matrix[$y][$x] !== $this::M_NULL) {
292 continue;
293 }
294 for ($ry = -2; $ry <= 2; $ry++) {
295 for ($rx = -2; $rx <= 2; $rx++) {
296 $v = $ry === 0 && $rx === 0 || $ry === 2 || $ry === -2 || $rx === 2 || $rx === -2;
297 $this->set($x + $rx, $y + $ry, $v, $this::M_ALIGNMENT);
298 }
299 }
300 }
301 }
302 return $this;
303 }
304 /**
305 * Draws the timing pattern (h/v checkered line between the finder patterns)
306 *
307 * @return \chillerlan\QRCode\Data\QRMatrix
308 */
309 public function setTimingPattern()
310 {
311 foreach (\range(8, $this->moduleCount - 8 - 1) as $i) {
312 if ($this->matrix[6][$i] !== $this::M_NULL || $this->matrix[$i][6] !== $this::M_NULL) {
313 continue;
314 }
315 $v = $i % 2 === 0;
316 $t = $this::M_TIMING;
317 $this->set($i, 6, $v, $t);
318 // h
319 $this->set(6, $i, $v, $t);
320 // v
321 }
322 return $this;
323 }
324 /**
325 * Draws the version information, 2x 3x6 pixel
326 *
327 * @param bool|null $test
328 *
329 * @return \chillerlan\QRCode\Data\QRMatrix
330 */
331 public function setVersionNumber($test = null)
332 {
333 $test = $test !== null ? $test : \false;
334 $bits = \array_key_exists($this->version, self::versionPattern) ? self::versionPattern[$this->version] : \false;
335 if ($bits !== \false) {
336 for ($i = 0; $i < 18; $i++) {
337 $a = (int) \floor($i / 3);
338 $b = $i % 3 + $this->moduleCount - 8 - 3;
339 $v = !$test && ($bits >> $i & 1) === 1;
340 $t = $this::M_VERSION;
341 $this->set($b, $a, $v, $t);
342 // ne
343 $this->set($a, $b, $v, $t);
344 // sw
345 }
346 }
347 return $this;
348 }
349 /**
350 * Draws the format info along the finder patterns
351 *
352 * @param int $maskPattern
353 * @param bool|null $test
354 *
355 * @return \chillerlan\QRCode\Data\QRMatrix
356 */
357 public function setFormatInfo($maskPattern, $test = null)
358 {
359 $test = $test !== null ? $test : \false;
360 $bits = \array_key_exists($this->eclevel, \YoastSEO_Vendor\chillerlan\QRCode\QRCode::ECC_MODES) && \array_key_exists($maskPattern, self::formatPattern[\YoastSEO_Vendor\chillerlan\QRCode\QRCode::ECC_MODES[$this->eclevel]]) ? self::formatPattern[\YoastSEO_Vendor\chillerlan\QRCode\QRCode::ECC_MODES[$this->eclevel]][$maskPattern] : 0;
361 $t = $this::M_FORMAT;
362 for ($i = 0; $i < 15; $i++) {
363 $v = !$test && ($bits >> $i & 1) === 1;
364 if ($i < 6) {
365 $this->set(8, $i, $v, $t);
366 } elseif ($i < 8) {
367 $this->set(8, $i + 1, $v, $t);
368 } else {
369 $this->set(8, $this->moduleCount - 15 + $i, $v, $t);
370 }
371 if ($i < 8) {
372 $this->set($this->moduleCount - $i - 1, 8, $v, $t);
373 } elseif ($i < 9) {
374 $this->set(15 - $i, 8, $v, $t);
375 } else {
376 $this->set(15 - $i - 1, 8, $v, $t);
377 }
378 }
379 $this->set(8, $this->moduleCount - 8, !$test, $t);
380 return $this;
381 }
382 /**
383 * Draws the "quiet zone" of $size around the matrix
384 *
385 * @param int|null $size
386 *
387 * @return \chillerlan\QRCode\Data\QRMatrix
388 * @throws \chillerlan\QRCode\Data\QRCodeDataException
389 */
390 public function setQuietZone($size = null)
391 {
392 if ($this->matrix[$this->moduleCount - 1][$this->moduleCount - 1] === $this::M_NULL) {
393 throw new \YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException('use only after writing data');
394 }
395 $size = $size !== null ? \max(0, \min($size, \floor($this->moduleCount / 2))) : 4;
396 $t = $this::M_QUIETZONE;
397 for ($y = 0; $y < $this->moduleCount; $y++) {
398 for ($i = 0; $i < $size; $i++) {
399 \array_unshift($this->matrix[$y], $t);
400 \array_push($this->matrix[$y], $t);
401 }
402 }
403 $this->moduleCount += $size * 2;
404 $r = \array_fill(0, $this->moduleCount, $t);
405 for ($i = 0; $i < $size; $i++) {
406 \array_unshift($this->matrix, $r);
407 \array_push($this->matrix, $r);
408 }
409 return $this;
410 }
411 /**
412 * Maps the binary $data array from QRDataInterface::maskECC() on the matrix, using $maskPattern
413 *
414 * @see \chillerlan\QRCode\Data\QRDataAbstract::maskECC()
415 *
416 * @param int[] $data
417 * @param int $maskPattern
418 *
419 * @return \chillerlan\QRCode\Data\QRMatrix
420 */
421 public function mapData(array $data, $maskPattern)
422 {
423 $this->maskPattern = $maskPattern;
424 $byteCount = \count($data);
425 $size = $this->moduleCount - 1;
426 $mask = $this->getMask($this->maskPattern);
427 for ($i = $size, $y = $size, $inc = -1, $byteIndex = 0, $bitIndex = 7; $i > 0; $i -= 2) {
428 if ($i === 6) {
429 $i--;
430 }
431 while (\true) {
432 for ($c = 0; $c < 2; $c++) {
433 $x = $i - $c;
434 if ($this->matrix[$y][$x] === $this::M_NULL) {
435 $v = \false;
436 if ($byteIndex < $byteCount) {
437 $v = ($data[$byteIndex] >> $bitIndex & 1) === 1;
438 }
439 if ($mask($x, $y) === 0) {
440 $v = !$v;
441 }
442 $this->matrix[$y][$x] = $this::M_DATA << ($v ? 8 : 0);
443 $bitIndex--;
444 if ($bitIndex === -1) {
445 $byteIndex++;
446 $bitIndex = 7;
447 }
448 }
449 }
450 $y += $inc;
451 if ($y < 0 || $this->moduleCount <= $y) {
452 $y -= $inc;
453 $inc = -$inc;
454 break;
455 }
456 }
457 }
458 return $this;
459 }
460 /**
461 * ISO/IEC 18004:2000 Section 8.8.1
462 *
463 * Note that some versions of the QR code standard have had errors in the section about mask patterns.
464 * The information below has been corrected. (https://www.thonky.com/qr-code-tutorial/mask-patterns)
465 *
466 * @see \chillerlan\QRCode\QRMatrix::mapData()
467 *
468 * @internal
469 *
470 * @param int $maskPattern
471 *
472 * @return \Closure
473 * @throws \chillerlan\QRCode\Data\QRCodeDataException
474 */
475 protected function getMask($maskPattern)
476 {
477 if ((0b111 & $maskPattern) !== $maskPattern) {
478 throw new \YoastSEO_Vendor\chillerlan\QRCode\Data\QRCodeDataException('invalid mask pattern');
479 // @codeCoverageIgnore
480 }
481 return [0b0 => function ($x, $y) {
482 return ($x + $y) % 2;
483 }, 0b1 => function ($x, $y) {
484 return $y % 2;
485 }, 0b10 => function ($x, $y) {
486 return $x % 3;
487 }, 0b11 => function ($x, $y) {
488 return ($x + $y) % 3;
489 }, 0b100 => function ($x, $y) {
490 return ((int) ($y / 2) + (int) ($x / 3)) % 2;
491 }, 0b101 => function ($x, $y) {
492 return $x * $y % 2 + $x * $y % 3;
493 }, 0b110 => function ($x, $y) {
494 return ($x * $y % 2 + $x * $y % 3) % 2;
495 }, 0b111 => function ($x, $y) {
496 return ($x * $y % 3 + ($x + $y) % 2) % 2;
497 }][$maskPattern];
498 }
499 }
500