PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / trunk
Matomo Analytics – Powerful, Privacy-First Insights for WordPress vtrunk
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / tecnickcom / tcpdf / include / barcodes / qrcode.php
matomo / app / vendor / tecnickcom / tcpdf / include / barcodes Last commit date
datamatrix.php 2 months ago pdf417.php 2 months ago qrcode.php 2 months ago
qrcode.php
2983 lines
1 <?php
2
3 namespace {
4 //============================================================+
5 // File name : qrcode.php
6 // Version : 1.0.010
7 // Begin : 2010-03-22
8 // Last Update : 2012-07-25
9 // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
10 // License : GNU-LGPL v3 (https://www.gnu.org/copyleft/lesser.html)
11 // -------------------------------------------------------------------
12 // Copyright (C) 2010-2026 Nicola Asuni - Tecnick.com LTD
13 //
14 // This file is part of TCPDF software library.
15 //
16 // TCPDF is free software: you can redistribute it and/or modify it
17 // under the terms of the GNU Lesser General Public License as
18 // published by the Free Software Foundation, either version 3 of the
19 // License, or (at your option) any later version.
20 //
21 // TCPDF is distributed in the hope that it will be useful, but
22 // WITHOUT ANY WARRANTY; without even the implied warranty of
23 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 // See the GNU Lesser General Public License for more details.
25 //
26 // You should have received a copy of the GNU Lesser General Public License
27 // along with TCPDF. If not, see <https://www.gnu.org/licenses/>.
28 //
29 // See LICENSE.TXT file for more information.
30 // -------------------------------------------------------------------
31 //
32 // DESCRIPTION :
33 //
34 // Class to create QR-code arrays for TCPDF class.
35 // QR Code symbol is a 2D barcode that can be scanned by
36 // handy terminals such as a mobile phone with CCD.
37 // The capacity of QR Code is up to 7000 digits or 4000
38 // characters, and has high robustness.
39 // This class supports QR Code model 2, described in
40 // JIS (Japanese Industrial Standards) X0510:2004
41 // or ISO/IEC 18004.
42 // Currently the following features are not supported:
43 // ECI and FNC1 mode, Micro QR Code, QR Code model 1,
44 // Structured mode.
45 //
46 // This class is derived from the following projects:
47 // ---------------------------------------------------------
48 // "PHP QR Code encoder"
49 // License: GNU-LGPLv3
50 // Copyright (C) 2010 by Dominik Dzienia <deltalab at poczta dot fm>
51 // http://phpqrcode.sourceforge.net/
52 // https://sourceforge.net/projects/phpqrcode/
53 //
54 // The "PHP QR Code encoder" is based on
55 // "C libqrencode library" (ver. 3.1.1)
56 // License: GNU-LGPL 2.1
57 // Copyright (C) 2006-2010 by Kentaro Fukuchi
58 // http://megaui.net/fukuchi/works/qrencode/index.en.html
59 //
60 // Reed-Solomon code encoder is written by Phil Karn, KA9Q.
61 // Copyright (C) 2002-2006 Phil Karn, KA9Q
62 //
63 // QR Code is registered trademark of DENSO WAVE INCORPORATED
64 // http://www.denso-wave.com/qrcode/index-e.html
65 // ---------------------------------------------------------
66 //============================================================+
67 /**
68 * @file
69 * Class to create QR-code arrays for TCPDF class.
70 * QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD.
71 * The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness.
72 * This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004.
73 * Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode.
74 *
75 * This class is derived from "PHP QR Code encoder" by Dominik Dzienia (http://phpqrcode.sourceforge.net/) based on "libqrencode C library 3.1.1." by Kentaro Fukuchi (http://megaui.net/fukuchi/works/qrencode/index.en.html), contains Reed-Solomon code written by Phil Karn, KA9Q. QR Code is registered trademark of DENSO WAVE INCORPORATED (http://www.denso-wave.com/qrcode/index-e.html).
76 * Please read comments on this class source file for full copyright and license information.
77 *
78 * @package com.tecnick.tcpdf
79 * @author Nicola Asuni
80 * @version 1.0.010
81 */
82 // definitions
83 if (!\defined('QRCODEDEFS')) {
84 /**
85 * Indicate that definitions for this class are set
86 */
87 \define('QRCODEDEFS', \true);
88 // -----------------------------------------------------
89 // Encoding modes (characters which can be encoded in QRcode)
90 /**
91 * Encoding mode
92 */
93 \define('QR_MODE_NL', -1);
94 /**
95 * Encoding mode numeric (0-9). 3 characters are encoded to 10bit length. In theory, 7089 characters or less can be stored in a QRcode.
96 */
97 \define('QR_MODE_NM', 0);
98 /**
99 * Encoding mode alphanumeric (0-9A-Z $%*+-./:) 45characters. 2 characters are encoded to 11bit length. In theory, 4296 characters or less can be stored in a QRcode.
100 */
101 \define('QR_MODE_AN', 1);
102 /**
103 * Encoding mode 8bit byte data. In theory, 2953 characters or less can be stored in a QRcode.
104 */
105 \define('QR_MODE_8B', 2);
106 /**
107 * Encoding mode KANJI. A KANJI character (multibyte character) is encoded to 13bit length. In theory, 1817 characters or less can be stored in a QRcode.
108 */
109 \define('QR_MODE_KJ', 3);
110 /**
111 * Encoding mode STRUCTURED (currently unsupported)
112 */
113 \define('QR_MODE_ST', 4);
114 // -----------------------------------------------------
115 // Levels of error correction.
116 // QRcode has a function of an error correcting for miss reading that white is black.
117 // Error correcting is defined in 4 level as below.
118 /**
119 * Error correction level L : About 7% or less errors can be corrected.
120 */
121 \define('QR_ECLEVEL_L', 0);
122 /**
123 * Error correction level M : About 15% or less errors can be corrected.
124 */
125 \define('QR_ECLEVEL_M', 1);
126 /**
127 * Error correction level Q : About 25% or less errors can be corrected.
128 */
129 \define('QR_ECLEVEL_Q', 2);
130 /**
131 * Error correction level H : About 30% or less errors can be corrected.
132 */
133 \define('QR_ECLEVEL_H', 3);
134 // -----------------------------------------------------
135 // Version. Size of QRcode is defined as version.
136 // Version is from 1 to 40.
137 // Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases.
138 // So version 40 is 177*177 matrix.
139 /**
140 * Maximum QR Code version.
141 */
142 \define('QRSPEC_VERSION_MAX', 40);
143 /**
144 * Maximum matrix size for maximum version (version 40 is 177*177 matrix).
145 */
146 \define('QRSPEC_WIDTH_MAX', 177);
147 // -----------------------------------------------------
148 /**
149 * Matrix index to get width from $capacity array.
150 */
151 \define('QRCAP_WIDTH', 0);
152 /**
153 * Matrix index to get number of words from $capacity array.
154 */
155 \define('QRCAP_WORDS', 1);
156 /**
157 * Matrix index to get remainder from $capacity array.
158 */
159 \define('QRCAP_REMINDER', 2);
160 /**
161 * Matrix index to get error correction level from $capacity array.
162 */
163 \define('QRCAP_EC', 3);
164 // -----------------------------------------------------
165 // Structure (currently usupported)
166 /**
167 * Number of header bits for structured mode
168 */
169 \define('STRUCTURE_HEADER_BITS', 20);
170 /**
171 * Max number of symbols for structured mode
172 */
173 \define('MAX_STRUCTURED_SYMBOLS', 16);
174 // -----------------------------------------------------
175 // Masks
176 /**
177 * Down point base value for case 1 mask pattern (concatenation of same color in a line or a column)
178 */
179 \define('N1', 3);
180 /**
181 * Down point base value for case 2 mask pattern (module block of same color)
182 */
183 \define('N2', 3);
184 /**
185 * Down point base value for case 3 mask pattern (1:1:3:1:1(dark:bright:dark:bright:dark)pattern in a line or a column)
186 */
187 \define('N3', 40);
188 /**
189 * Down point base value for case 4 mask pattern (ration of dark modules in whole)
190 */
191 \define('N4', 10);
192 // -----------------------------------------------------
193 // Optimization settings
194 /**
195 * if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
196 */
197 \define('QR_FIND_BEST_MASK', \true);
198 /**
199 * if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
200 */
201 \define('QR_FIND_FROM_RANDOM', 2);
202 /**
203 * when QR_FIND_BEST_MASK === false
204 */
205 \define('QR_DEFAULT_MASK', 2);
206 // -----------------------------------------------------
207 }
208 // end of definitions
209 /**
210 * @class QRcode
211 * Class to create QR-code arrays for TCPDF class.
212 * QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD.
213 * The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness.
214 * This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004.
215 * Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode.
216 *
217 * This class is derived from "PHP QR Code encoder" by Dominik Dzienia (http://phpqrcode.sourceforge.net/) based on "libqrencode C library 3.1.1." by Kentaro Fukuchi (http://megaui.net/fukuchi/works/qrencode/index.en.html), contains Reed-Solomon code written by Phil Karn, KA9Q. QR Code is registered trademark of DENSO WAVE INCORPORATED (http://www.denso-wave.com/qrcode/index-e.html).
218 * Please read comments on this class source file for full copyright and license information.
219 *
220 * @package com.tecnick.tcpdf
221 * @author Nicola Asuni
222 * @version 1.0.010
223 */
224 class QRcode
225 {
226 /**
227 * Barcode array to be returned which is readable by TCPDF.
228 * @protected
229 */
230 protected $barcode_array = array();
231 /**
232 * QR code version. Size of QRcode is defined as version. Version is from 1 to 40. Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases. So version 40 is 177*177 matrix.
233 * @protected
234 */
235 protected $version = 0;
236 /**
237 * Levels of error correction. See definitions for possible values.
238 * @protected
239 */
240 protected $level = \QR_ECLEVEL_L;
241 /**
242 * Encoding mode.
243 * @protected
244 */
245 protected $hint = \QR_MODE_8B;
246 /**
247 * Boolean flag, if true the input string will be converted to uppercase.
248 * @protected
249 */
250 protected $casesensitive = \true;
251 /**
252 * Structured QR code (not supported yet).
253 * @protected
254 */
255 protected $structured = 0;
256 /**
257 * Mask data.
258 * @protected
259 */
260 protected $data;
261 // FrameFiller
262 /**
263 * Width.
264 * @protected
265 */
266 protected $width;
267 /**
268 * Frame.
269 * @protected
270 */
271 protected $frame;
272 /**
273 * X position of bit.
274 * @protected
275 */
276 protected $x;
277 /**
278 * Y position of bit.
279 * @protected
280 */
281 protected $y;
282 /**
283 * Direction.
284 * @protected
285 */
286 protected $dir;
287 /**
288 * Single bit value.
289 * @protected
290 */
291 protected $bit;
292 // ---- QRrawcode ----
293 /**
294 * Data code.
295 * @protected
296 */
297 protected $datacode = array();
298 /**
299 * Error correction code.
300 * @protected
301 */
302 protected $ecccode = array();
303 /**
304 * Blocks.
305 * @protected
306 */
307 protected $blocks;
308 /**
309 * Reed-Solomon blocks.
310 * @protected
311 */
312 protected $rsblocks = array();
313 //of RSblock
314 /**
315 * Counter.
316 * @protected
317 */
318 protected $count;
319 /**
320 * Data length.
321 * @protected
322 */
323 protected $dataLength;
324 /**
325 * Error correction length.
326 * @protected
327 */
328 protected $eccLength;
329 /**
330 * Value b1.
331 * @protected
332 */
333 protected $b1;
334 // ---- QRmask ----
335 /**
336 * Run length.
337 * @protected
338 */
339 protected $runLength = array();
340 // ---- QRsplit ----
341 /**
342 * Input data string.
343 * @protected
344 */
345 protected $dataStr = '';
346 /**
347 * Input items.
348 * @protected
349 */
350 protected $items;
351 // Reed-Solomon items
352 /**
353 * Reed-Solomon items.
354 * @protected
355 */
356 protected $rsitems = array();
357 /**
358 * Array of frames.
359 * @protected
360 */
361 protected $frames = array();
362 /**
363 * Alphabet-numeric convesion table.
364 * @protected
365 */
366 protected $anTable = array(
367 -1,
368 -1,
369 -1,
370 -1,
371 -1,
372 -1,
373 -1,
374 -1,
375 -1,
376 -1,
377 -1,
378 -1,
379 -1,
380 -1,
381 -1,
382 -1,
383 //
384 -1,
385 -1,
386 -1,
387 -1,
388 -1,
389 -1,
390 -1,
391 -1,
392 -1,
393 -1,
394 -1,
395 -1,
396 -1,
397 -1,
398 -1,
399 -1,
400 //
401 36,
402 -1,
403 -1,
404 -1,
405 37,
406 38,
407 -1,
408 -1,
409 -1,
410 -1,
411 39,
412 40,
413 -1,
414 41,
415 42,
416 43,
417 //
418 0,
419 1,
420 2,
421 3,
422 4,
423 5,
424 6,
425 7,
426 8,
427 9,
428 44,
429 -1,
430 -1,
431 -1,
432 -1,
433 -1,
434 //
435 -1,
436 10,
437 11,
438 12,
439 13,
440 14,
441 15,
442 16,
443 17,
444 18,
445 19,
446 20,
447 21,
448 22,
449 23,
450 24,
451 //
452 25,
453 26,
454 27,
455 28,
456 29,
457 30,
458 31,
459 32,
460 33,
461 34,
462 35,
463 -1,
464 -1,
465 -1,
466 -1,
467 -1,
468 //
469 -1,
470 -1,
471 -1,
472 -1,
473 -1,
474 -1,
475 -1,
476 -1,
477 -1,
478 -1,
479 -1,
480 -1,
481 -1,
482 -1,
483 -1,
484 -1,
485 //
486 -1,
487 -1,
488 -1,
489 -1,
490 -1,
491 -1,
492 -1,
493 -1,
494 -1,
495 -1,
496 -1,
497 -1,
498 -1,
499 -1,
500 -1,
501 -1,
502 );
503 /**
504 * Array Table of the capacity of symbols.
505 * See Table 1 (pp.13) and Table 12-16 (pp.30-36), JIS X0510:2004.
506 * @protected
507 */
508 protected $capacity = array(
509 array(0, 0, 0, array(0, 0, 0, 0)),
510 //
511 array(21, 26, 0, array(7, 10, 13, 17)),
512 // 1
513 array(25, 44, 7, array(10, 16, 22, 28)),
514 //
515 array(29, 70, 7, array(15, 26, 36, 44)),
516 //
517 array(33, 100, 7, array(20, 36, 52, 64)),
518 //
519 array(37, 134, 7, array(26, 48, 72, 88)),
520 // 5
521 array(41, 172, 7, array(36, 64, 96, 112)),
522 //
523 array(45, 196, 0, array(40, 72, 108, 130)),
524 //
525 array(49, 242, 0, array(48, 88, 132, 156)),
526 //
527 array(53, 292, 0, array(60, 110, 160, 192)),
528 //
529 array(57, 346, 0, array(72, 130, 192, 224)),
530 // 10
531 array(61, 404, 0, array(80, 150, 224, 264)),
532 //
533 array(65, 466, 0, array(96, 176, 260, 308)),
534 //
535 array(69, 532, 0, array(104, 198, 288, 352)),
536 //
537 array(73, 581, 3, array(120, 216, 320, 384)),
538 //
539 array(77, 655, 3, array(132, 240, 360, 432)),
540 // 15
541 array(81, 733, 3, array(144, 280, 408, 480)),
542 //
543 array(85, 815, 3, array(168, 308, 448, 532)),
544 //
545 array(89, 901, 3, array(180, 338, 504, 588)),
546 //
547 array(93, 991, 3, array(196, 364, 546, 650)),
548 //
549 array(97, 1085, 3, array(224, 416, 600, 700)),
550 // 20
551 array(101, 1156, 4, array(224, 442, 644, 750)),
552 //
553 array(105, 1258, 4, array(252, 476, 690, 816)),
554 //
555 array(109, 1364, 4, array(270, 504, 750, 900)),
556 //
557 array(113, 1474, 4, array(300, 560, 810, 960)),
558 //
559 array(117, 1588, 4, array(312, 588, 870, 1050)),
560 // 25
561 array(121, 1706, 4, array(336, 644, 952, 1110)),
562 //
563 array(125, 1828, 4, array(360, 700, 1020, 1200)),
564 //
565 array(129, 1921, 3, array(390, 728, 1050, 1260)),
566 //
567 array(133, 2051, 3, array(420, 784, 1140, 1350)),
568 //
569 array(137, 2185, 3, array(450, 812, 1200, 1440)),
570 // 30
571 array(141, 2323, 3, array(480, 868, 1290, 1530)),
572 //
573 array(145, 2465, 3, array(510, 924, 1350, 1620)),
574 //
575 array(149, 2611, 3, array(540, 980, 1440, 1710)),
576 //
577 array(153, 2761, 3, array(570, 1036, 1530, 1800)),
578 //
579 array(157, 2876, 0, array(570, 1064, 1590, 1890)),
580 // 35
581 array(161, 3034, 0, array(600, 1120, 1680, 1980)),
582 //
583 array(165, 3196, 0, array(630, 1204, 1770, 2100)),
584 //
585 array(169, 3362, 0, array(660, 1260, 1860, 2220)),
586 //
587 array(173, 3532, 0, array(720, 1316, 1950, 2310)),
588 //
589 array(177, 3706, 0, array(750, 1372, 2040, 2430)),
590 );
591 /**
592 * Array Length indicator.
593 * @protected
594 */
595 protected $lengthTableBits = array(array(10, 12, 14), array(9, 11, 13), array(8, 16, 16), array(8, 10, 12));
596 /**
597 * Array Table of the error correction code (Reed-Solomon block).
598 * See Table 12-16 (pp.30-36), JIS X0510:2004.
599 * @protected
600 */
601 protected $eccTable = array(
602 array(array(0, 0), array(0, 0), array(0, 0), array(0, 0)),
603 //
604 array(array(1, 0), array(1, 0), array(1, 0), array(1, 0)),
605 // 1
606 array(array(1, 0), array(1, 0), array(1, 0), array(1, 0)),
607 //
608 array(array(1, 0), array(1, 0), array(2, 0), array(2, 0)),
609 //
610 array(array(1, 0), array(2, 0), array(2, 0), array(4, 0)),
611 //
612 array(array(1, 0), array(2, 0), array(2, 2), array(2, 2)),
613 // 5
614 array(array(2, 0), array(4, 0), array(4, 0), array(4, 0)),
615 //
616 array(array(2, 0), array(4, 0), array(2, 4), array(4, 1)),
617 //
618 array(array(2, 0), array(2, 2), array(4, 2), array(4, 2)),
619 //
620 array(array(2, 0), array(3, 2), array(4, 4), array(4, 4)),
621 //
622 array(array(2, 2), array(4, 1), array(6, 2), array(6, 2)),
623 // 10
624 array(array(4, 0), array(1, 4), array(4, 4), array(3, 8)),
625 //
626 array(array(2, 2), array(6, 2), array(4, 6), array(7, 4)),
627 //
628 array(array(4, 0), array(8, 1), array(8, 4), array(12, 4)),
629 //
630 array(array(3, 1), array(4, 5), array(11, 5), array(11, 5)),
631 //
632 array(array(5, 1), array(5, 5), array(5, 7), array(11, 7)),
633 // 15
634 array(array(5, 1), array(7, 3), array(15, 2), array(3, 13)),
635 //
636 array(array(1, 5), array(10, 1), array(1, 15), array(2, 17)),
637 //
638 array(array(5, 1), array(9, 4), array(17, 1), array(2, 19)),
639 //
640 array(array(3, 4), array(3, 11), array(17, 4), array(9, 16)),
641 //
642 array(array(3, 5), array(3, 13), array(15, 5), array(15, 10)),
643 // 20
644 array(array(4, 4), array(17, 0), array(17, 6), array(19, 6)),
645 //
646 array(array(2, 7), array(17, 0), array(7, 16), array(34, 0)),
647 //
648 array(array(4, 5), array(4, 14), array(11, 14), array(16, 14)),
649 //
650 array(array(6, 4), array(6, 14), array(11, 16), array(30, 2)),
651 //
652 array(array(8, 4), array(8, 13), array(7, 22), array(22, 13)),
653 // 25
654 array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)),
655 //
656 array(array(8, 4), array(22, 3), array(8, 26), array(12, 28)),
657 //
658 array(array(3, 10), array(3, 23), array(4, 31), array(11, 31)),
659 //
660 array(array(7, 7), array(21, 7), array(1, 37), array(19, 26)),
661 //
662 array(array(5, 10), array(19, 10), array(15, 25), array(23, 25)),
663 // 30
664 array(array(13, 3), array(2, 29), array(42, 1), array(23, 28)),
665 //
666 array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)),
667 //
668 array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)),
669 //
670 array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)),
671 //
672 array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)),
673 // 35
674 array(array(6, 14), array(6, 34), array(46, 10), array(2, 64)),
675 //
676 array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)),
677 //
678 array(array(4, 18), array(13, 32), array(48, 14), array(42, 32)),
679 //
680 array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)),
681 //
682 array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),
683 );
684 /**
685 * Array Positions of alignment patterns.
686 * This array includes only the second and the third position of the alignment patterns. Rest of them can be calculated from the distance between them.
687 * See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
688 * @protected
689 */
690 protected $alignmentPattern = array(
691 array(0, 0),
692 array(0, 0),
693 array(18, 0),
694 array(22, 0),
695 array(26, 0),
696 array(30, 0),
697 // 1- 5
698 array(34, 0),
699 array(22, 38),
700 array(24, 42),
701 array(26, 46),
702 array(28, 50),
703 // 6-10
704 array(30, 54),
705 array(32, 58),
706 array(34, 62),
707 array(26, 46),
708 array(26, 48),
709 // 11-15
710 array(26, 50),
711 array(30, 54),
712 array(30, 56),
713 array(30, 58),
714 array(34, 62),
715 // 16-20
716 array(28, 50),
717 array(26, 50),
718 array(30, 54),
719 array(28, 54),
720 array(32, 58),
721 // 21-25
722 array(30, 58),
723 array(34, 62),
724 array(26, 50),
725 array(30, 54),
726 array(26, 52),
727 // 26-30
728 array(30, 56),
729 array(34, 60),
730 array(30, 58),
731 array(34, 62),
732 array(30, 54),
733 // 31-35
734 array(24, 50),
735 array(28, 54),
736 array(32, 58),
737 array(26, 54),
738 array(30, 58),
739 );
740 /**
741 * Array Version information pattern (BCH coded).
742 * See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
743 * size: [QRSPEC_VERSION_MAX - 6]
744 * @protected
745 */
746 protected $versionPattern = array(
747 0x7c94,
748 0x85bc,
749 0x9a99,
750 0xa4d3,
751 0xbbf6,
752 0xc762,
753 0xd847,
754 0xe60d,
755 //
756 0xf928,
757 0x10b78,
758 0x1145d,
759 0x12a17,
760 0x13532,
761 0x149a6,
762 0x15683,
763 0x168c9,
764 //
765 0x177ec,
766 0x18ec4,
767 0x191e1,
768 0x1afab,
769 0x1b08e,
770 0x1cc1a,
771 0x1d33f,
772 0x1ed75,
773 //
774 0x1f250,
775 0x209d5,
776 0x216f0,
777 0x228ba,
778 0x2379f,
779 0x24b0b,
780 0x2542e,
781 0x26a64,
782 //
783 0x27541,
784 0x28c69,
785 );
786 /**
787 * Array Format information
788 * @protected
789 */
790 protected $formatInfo = array(
791 array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976),
792 //
793 array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0),
794 //
795 array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed),
796 //
797 array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x762, 0x255, 0xd0c, 0x83b),
798 );
799 // -------------------------------------------------
800 // -------------------------------------------------
801 /**
802 * This is the class constructor.
803 * Creates a QRcode object
804 * @param string $code code to represent using QRcode
805 * @param string $eclevel error level: <ul><li>L : About 7% or less errors can be corrected.</li><li>M : About 15% or less errors can be corrected.</li><li>Q : About 25% or less errors can be corrected.</li><li>H : About 30% or less errors can be corrected.</li></ul>
806 * @public
807 * @since 1.0.000
808 */
809 public function __construct($code, $eclevel = 'L')
810 {
811 $barcode_array = array();
812 if (\is_null($code) or $code == '\\0' or $code == '') {
813 return \false;
814 }
815 // set error correction level
816 $this->level = \array_search($eclevel, array('L', 'M', 'Q', 'H'));
817 if ($this->level === \false) {
818 $this->level = \QR_ECLEVEL_L;
819 }
820 if ($this->hint != \QR_MODE_8B and $this->hint != \QR_MODE_KJ) {
821 return \false;
822 }
823 if ($this->version < 0 or $this->version > \QRSPEC_VERSION_MAX) {
824 return \false;
825 }
826 $this->items = array();
827 $this->encodeString($code);
828 if (\is_null($this->data)) {
829 return \false;
830 }
831 $qrTab = $this->binarize($this->data);
832 $size = \count($qrTab);
833 $barcode_array['num_rows'] = $size;
834 $barcode_array['num_cols'] = $size;
835 $barcode_array['bcode'] = array();
836 foreach ($qrTab as $line) {
837 $arrAdd = array();
838 foreach (\str_split($line) as $char) {
839 $arrAdd[] = $char == '1' ? 1 : 0;
840 }
841 $barcode_array['bcode'][] = $arrAdd;
842 }
843 $this->barcode_array = $barcode_array;
844 }
845 /**
846 * Returns a barcode array which is readable by TCPDF
847 * @return array barcode array readable by TCPDF;
848 * @public
849 */
850 public function getBarcodeArray()
851 {
852 return $this->barcode_array;
853 }
854 /**
855 * Convert the frame in binary form
856 * @param array $frame array to binarize
857 * @return array frame in binary form
858 */
859 protected function binarize($frame)
860 {
861 $len = \count($frame);
862 // the frame is square (width = height)
863 foreach ($frame as &$frameLine) {
864 for ($i = 0; $i < $len; $i++) {
865 $frameLine[$i] = \ord($frameLine[$i]) & 1 ? '1' : '0';
866 }
867 }
868 return $frame;
869 }
870 /**
871 * Encode the input string to QR code
872 * @param string $string input string to encode
873 */
874 protected function encodeString($string)
875 {
876 $this->dataStr = $string;
877 if (!$this->casesensitive) {
878 $this->toUpper();
879 }
880 $ret = $this->splitString();
881 if ($ret < 0) {
882 return NULL;
883 }
884 $this->encodeMask(-1);
885 }
886 /**
887 * Encode mask
888 * @param int $mask masking mode
889 */
890 protected function encodeMask($mask)
891 {
892 $spec = array(0, 0, 0, 0, 0);
893 $this->datacode = $this->getByteStream($this->items);
894 if (\is_null($this->datacode)) {
895 return NULL;
896 }
897 $spec = $this->getEccSpec($this->version, $this->level, $spec);
898 $this->b1 = $this->rsBlockNum1($spec);
899 $this->dataLength = $this->rsDataLength($spec);
900 $this->eccLength = $this->rsEccLength($spec);
901 $this->ecccode = \array_fill(0, $this->eccLength, 0);
902 $this->blocks = $this->rsBlockNum($spec);
903 $ret = $this->init($spec);
904 if ($ret < 0) {
905 return NULL;
906 }
907 $this->count = 0;
908 $this->width = $this->getWidth($this->version);
909 $this->frame = $this->newFrame($this->version);
910 $this->x = $this->width - 1;
911 $this->y = $this->width - 1;
912 $this->dir = -1;
913 $this->bit = -1;
914 // inteleaved data and ecc codes
915 for ($i = 0; $i < $this->dataLength + $this->eccLength; $i++) {
916 $code = $this->getCode();
917 $bit = 0x80;
918 for ($j = 0; $j < 8; $j++) {
919 $addr = $this->getNextPosition();
920 $this->setFrameAt($addr, 0x2 | ($bit & $code) != 0);
921 $bit = $bit >> 1;
922 }
923 }
924 // remainder bits
925 $j = $this->getRemainder($this->version);
926 for ($i = 0; $i < $j; $i++) {
927 $addr = $this->getNextPosition();
928 $this->setFrameAt($addr, 0x2);
929 }
930 // masking
931 $this->runLength = \array_fill(0, \QRSPEC_WIDTH_MAX + 1, 0);
932 if ($mask < 0) {
933 if (\QR_FIND_BEST_MASK) {
934 $masked = $this->mask($this->width, $this->frame, $this->level);
935 } else {
936 $masked = $this->makeMask($this->width, $this->frame, \intval(\QR_DEFAULT_MASK) % 8, $this->level);
937 }
938 } else {
939 $masked = $this->makeMask($this->width, $this->frame, $mask, $this->level);
940 }
941 if ($masked == NULL) {
942 return NULL;
943 }
944 $this->data = $masked;
945 }
946 // - - - - - - - - - - - - - - - - - - - - - - - - -
947 // FrameFiller
948 /**
949 * Set frame value at specified position
950 * @param array $at x,y position
951 * @param int $val value of the character to set
952 */
953 protected function setFrameAt($at, $val)
954 {
955 $this->frame[$at['y']][$at['x']] = \chr($val);
956 }
957 /**
958 * Get frame value at specified position
959 * @param array $at x,y position
960 * @return value at specified position
961 */
962 protected function getFrameAt($at)
963 {
964 return \ord($this->frame[$at['y']][$at['x']]);
965 }
966 /**
967 * Return the next frame position
968 * @return array of x,y coordinates
969 */
970 protected function getNextPosition()
971 {
972 do {
973 if ($this->bit == -1) {
974 $this->bit = 0;
975 return array('x' => $this->x, 'y' => $this->y);
976 }
977 $x = $this->x;
978 $y = $this->y;
979 $w = $this->width;
980 if ($this->bit == 0) {
981 $x--;
982 $this->bit++;
983 } else {
984 $x++;
985 $y += $this->dir;
986 $this->bit--;
987 }
988 if ($this->dir < 0) {
989 if ($y < 0) {
990 $y = 0;
991 $x -= 2;
992 $this->dir = 1;
993 if ($x == 6) {
994 $x--;
995 $y = 9;
996 }
997 }
998 } else {
999 if ($y == $w) {
1000 $y = $w - 1;
1001 $x -= 2;
1002 $this->dir = -1;
1003 if ($x == 6) {
1004 $x--;
1005 $y -= 8;
1006 }
1007 }
1008 }
1009 if ($x < 0 or $y < 0) {
1010 return NULL;
1011 }
1012 $this->x = $x;
1013 $this->y = $y;
1014 } while (\ord($this->frame[$y][$x]) & 0x80);
1015 return array('x' => $x, 'y' => $y);
1016 }
1017 // - - - - - - - - - - - - - - - - - - - - - - - - -
1018 // QRrawcode
1019 /**
1020 * Initialize code.
1021 * @param array $spec array of ECC specification
1022 * @return int 0 in case of success, -1 in case of error
1023 */
1024 protected function init($spec)
1025 {
1026 $dl = $this->rsDataCodes1($spec);
1027 $el = $this->rsEccCodes1($spec);
1028 $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
1029 $blockNo = 0;
1030 $dataPos = 0;
1031 $eccPos = 0;
1032 $endfor = $this->rsBlockNum1($spec);
1033 for ($i = 0; $i < $endfor; ++$i) {
1034 $ecc = \array_slice($this->ecccode, $eccPos);
1035 $this->rsblocks[$blockNo] = array();
1036 $this->rsblocks[$blockNo]['dataLength'] = $dl;
1037 $this->rsblocks[$blockNo]['data'] = \array_slice($this->datacode, $dataPos);
1038 $this->rsblocks[$blockNo]['eccLength'] = $el;
1039 $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
1040 $this->rsblocks[$blockNo]['ecc'] = $ecc;
1041 $this->ecccode = \array_merge(\array_slice($this->ecccode, 0, $eccPos), $ecc);
1042 $dataPos += $dl;
1043 $eccPos += $el;
1044 $blockNo++;
1045 }
1046 if ($this->rsBlockNum2($spec) == 0) {
1047 return 0;
1048 }
1049 $dl = $this->rsDataCodes2($spec);
1050 $el = $this->rsEccCodes2($spec);
1051 $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
1052 if ($rs == NULL) {
1053 return -1;
1054 }
1055 $endfor = $this->rsBlockNum2($spec);
1056 for ($i = 0; $i < $endfor; ++$i) {
1057 $ecc = \array_slice($this->ecccode, $eccPos);
1058 $this->rsblocks[$blockNo] = array();
1059 $this->rsblocks[$blockNo]['dataLength'] = $dl;
1060 $this->rsblocks[$blockNo]['data'] = \array_slice($this->datacode, $dataPos);
1061 $this->rsblocks[$blockNo]['eccLength'] = $el;
1062 $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
1063 $this->rsblocks[$blockNo]['ecc'] = $ecc;
1064 $this->ecccode = \array_merge(\array_slice($this->ecccode, 0, $eccPos), $ecc);
1065 $dataPos += $dl;
1066 $eccPos += $el;
1067 $blockNo++;
1068 }
1069 return 0;
1070 }
1071 /**
1072 * Return Reed-Solomon block code.
1073 * @return array rsblocks
1074 */
1075 protected function getCode()
1076 {
1077 if ($this->count < $this->dataLength) {
1078 $row = $this->count % $this->blocks;
1079 $col = (int) ($this->count / $this->blocks);
1080 if ($col >= $this->rsblocks[0]['dataLength']) {
1081 $row += $this->b1;
1082 }
1083 $row = (int) $row;
1084 $ret = $this->rsblocks[$row]['data'][$col];
1085 } elseif ($this->count < $this->dataLength + $this->eccLength) {
1086 $row = ($this->count - $this->dataLength) % $this->blocks;
1087 $col = (int) (($this->count - $this->dataLength) / $this->blocks);
1088 $ret = $this->rsblocks[$row]['ecc'][$col];
1089 } else {
1090 return 0;
1091 }
1092 $this->count++;
1093 return $ret;
1094 }
1095 // - - - - - - - - - - - - - - - - - - - - - - - - -
1096 // QRmask
1097 /**
1098 * Write Format Information on frame and returns the number of black bits
1099 * @param int $width frame width
1100 * @param array $frame frame
1101 * @param array $mask masking mode
1102 * @param int $level error correction level
1103 * @return int blacks
1104 */
1105 protected function writeFormatInformation($width, &$frame, $mask, $level)
1106 {
1107 $blacks = 0;
1108 $format = $this->getFormatInfo($mask, $level);
1109 for ($i = 0; $i < 8; ++$i) {
1110 if ($format & 1) {
1111 $blacks += 2;
1112 $v = 0x85;
1113 } else {
1114 $v = 0x84;
1115 }
1116 $frame[8][$width - 1 - $i] = \chr($v);
1117 if ($i < 6) {
1118 $frame[$i][8] = \chr($v);
1119 } else {
1120 $frame[$i + 1][8] = \chr($v);
1121 }
1122 $format = $format >> 1;
1123 }
1124 for ($i = 0; $i < 7; ++$i) {
1125 if ($format & 1) {
1126 $blacks += 2;
1127 $v = 0x85;
1128 } else {
1129 $v = 0x84;
1130 }
1131 $frame[$width - 7 + $i][8] = \chr($v);
1132 if ($i == 0) {
1133 $frame[8][7] = \chr($v);
1134 } else {
1135 $frame[8][6 - $i] = \chr($v);
1136 }
1137 $format = $format >> 1;
1138 }
1139 return $blacks;
1140 }
1141 /**
1142 * mask0
1143 * @param int $x X position
1144 * @param int $y Y position
1145 * @return int mask
1146 */
1147 protected function mask0($x, $y)
1148 {
1149 return $x + $y & 1;
1150 }
1151 /**
1152 * mask1
1153 * @param int $x X position
1154 * @param int $y Y position
1155 * @return int mask
1156 */
1157 protected function mask1($x, $y)
1158 {
1159 return $y & 1;
1160 }
1161 /**
1162 * mask2
1163 * @param int $x X position
1164 * @param int $y Y position
1165 * @return int mask
1166 */
1167 protected function mask2($x, $y)
1168 {
1169 return $x % 3;
1170 }
1171 /**
1172 * mask3
1173 * @param int $x X position
1174 * @param int $y Y position
1175 * @return int mask
1176 */
1177 protected function mask3($x, $y)
1178 {
1179 return ($x + $y) % 3;
1180 }
1181 /**
1182 * mask4
1183 * @param int $x X position
1184 * @param int $y Y position
1185 * @return int mask
1186 */
1187 protected function mask4($x, $y)
1188 {
1189 return (int) ($y / 2) + (int) ($x / 3) & 1;
1190 }
1191 /**
1192 * mask5
1193 * @param int $x X position
1194 * @param int $y Y position
1195 * @return int mask
1196 */
1197 protected function mask5($x, $y)
1198 {
1199 return ($x * $y & 1) + $x * $y % 3;
1200 }
1201 /**
1202 * mask6
1203 * @param int $x X position
1204 * @param int $y Y position
1205 * @return int mask
1206 */
1207 protected function mask6($x, $y)
1208 {
1209 return ($x * $y & 1) + $x * $y % 3 & 1;
1210 }
1211 /**
1212 * mask7
1213 * @param int $x X position
1214 * @param int $y Y position
1215 * @return int mask
1216 */
1217 protected function mask7($x, $y)
1218 {
1219 return $x * $y % 3 + ($x + $y & 1) & 1;
1220 }
1221 /**
1222 * Return bitmask
1223 * @param int $maskNo mask number
1224 * @param int $width width
1225 * @param array $frame frame
1226 * @return array bitmask
1227 */
1228 protected function generateMaskNo($maskNo, $width, $frame)
1229 {
1230 $bitMask = \array_fill(0, $width, \array_fill(0, $width, 0));
1231 for ($y = 0; $y < $width; ++$y) {
1232 for ($x = 0; $x < $width; ++$x) {
1233 if (\ord($frame[$y][$x]) & 0x80) {
1234 $bitMask[$y][$x] = 0;
1235 } else {
1236 $maskFunc = \call_user_func(array($this, 'mask' . $maskNo), $x, $y);
1237 $bitMask[$y][$x] = $maskFunc == 0 ? 1 : 0;
1238 }
1239 }
1240 }
1241 return $bitMask;
1242 }
1243 /**
1244 * makeMaskNo
1245 * @param int $maskNo
1246 * @param int $width
1247 * @param int $s
1248 * @param int $d
1249 * @param boolean $maskGenOnly
1250 * @return int b
1251 */
1252 protected function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = \false)
1253 {
1254 $b = 0;
1255 $bitMask = array();
1256 $bitMask = $this->generateMaskNo($maskNo, $width, $s);
1257 if ($maskGenOnly) {
1258 return;
1259 }
1260 $d = $s;
1261 for ($y = 0; $y < $width; ++$y) {
1262 for ($x = 0; $x < $width; ++$x) {
1263 if ($bitMask[$y][$x] == 1) {
1264 $d[$y][$x] = \chr(\ord($s[$y][$x]) ^ (int) $bitMask[$y][$x]);
1265 }
1266 $b += (int) (\ord($d[$y][$x]) & 1);
1267 }
1268 }
1269 return $b;
1270 }
1271 /**
1272 * makeMask
1273 * @param int $width
1274 * @param array $frame
1275 * @param int $maskNo
1276 * @param int $level
1277 * @return array mask
1278 */
1279 protected function makeMask($width, $frame, $maskNo, $level)
1280 {
1281 $masked = \array_fill(0, $width, \str_repeat("\x00", $width));
1282 $this->makeMaskNo($maskNo, $width, $frame, $masked);
1283 $this->writeFormatInformation($width, $masked, $maskNo, $level);
1284 return $masked;
1285 }
1286 /**
1287 * calcN1N3
1288 * @param int $length
1289 * @return int demerit
1290 */
1291 protected function calcN1N3($length)
1292 {
1293 $demerit = 0;
1294 for ($i = 0; $i < $length; ++$i) {
1295 if ($this->runLength[$i] >= 5) {
1296 $demerit += \N1 + ($this->runLength[$i] - 5);
1297 }
1298 if ($i & 1) {
1299 if ($i >= 3 and $i < $length - 2 and $this->runLength[$i] % 3 == 0) {
1300 $fact = (int) ($this->runLength[$i] / 3);
1301 if ($this->runLength[$i - 2] == $fact and $this->runLength[$i - 1] == $fact and $this->runLength[$i + 1] == $fact and $this->runLength[$i + 2] == $fact) {
1302 if ($this->runLength[$i - 3] < 0 or $this->runLength[$i - 3] >= 4 * $fact) {
1303 $demerit += \N3;
1304 } elseif ($i + 3 >= $length or $this->runLength[$i + 3] >= 4 * $fact) {
1305 $demerit += \N3;
1306 }
1307 }
1308 }
1309 }
1310 }
1311 return $demerit;
1312 }
1313 /**
1314 * evaluateSymbol
1315 * @param int $width
1316 * @param array $frame
1317 * @return int demerit
1318 */
1319 protected function evaluateSymbol($width, $frame)
1320 {
1321 $head = 0;
1322 $demerit = 0;
1323 for ($y = 0; $y < $width; ++$y) {
1324 $head = 0;
1325 $this->runLength[0] = 1;
1326 $frameY = $frame[$y];
1327 if ($y > 0) {
1328 $frameYM = $frame[$y - 1];
1329 }
1330 for ($x = 0; $x < $width; ++$x) {
1331 if ($x > 0 and $y > 0) {
1332 $b22 = \ord($frameY[$x]) & \ord($frameY[$x - 1]) & \ord($frameYM[$x]) & \ord($frameYM[$x - 1]);
1333 $w22 = \ord($frameY[$x]) | \ord($frameY[$x - 1]) | \ord($frameYM[$x]) | \ord($frameYM[$x - 1]);
1334 if (($b22 | $w22 ^ 1) & 1) {
1335 $demerit += \N2;
1336 }
1337 }
1338 if ($x == 0 and \ord($frameY[$x]) & 1) {
1339 $this->runLength[0] = -1;
1340 $head = 1;
1341 $this->runLength[$head] = 1;
1342 } elseif ($x > 0) {
1343 if ((\ord($frameY[$x]) ^ \ord($frameY[$x - 1])) & 1) {
1344 $head++;
1345 $this->runLength[$head] = 1;
1346 } else {
1347 $this->runLength[$head]++;
1348 }
1349 }
1350 }
1351 $demerit += $this->calcN1N3($head + 1);
1352 }
1353 for ($x = 0; $x < $width; ++$x) {
1354 $head = 0;
1355 $this->runLength[0] = 1;
1356 for ($y = 0; $y < $width; ++$y) {
1357 if ($y == 0 and \ord($frame[$y][$x]) & 1) {
1358 $this->runLength[0] = -1;
1359 $head = 1;
1360 $this->runLength[$head] = 1;
1361 } elseif ($y > 0) {
1362 if ((\ord($frame[$y][$x]) ^ \ord($frame[$y - 1][$x])) & 1) {
1363 $head++;
1364 $this->runLength[$head] = 1;
1365 } else {
1366 $this->runLength[$head]++;
1367 }
1368 }
1369 }
1370 $demerit += $this->calcN1N3($head + 1);
1371 }
1372 return $demerit;
1373 }
1374 /**
1375 * mask
1376 * @param int $width
1377 * @param array $frame
1378 * @param int $level
1379 * @return array best mask
1380 */
1381 protected function mask($width, $frame, $level)
1382 {
1383 $minDemerit = \PHP_INT_MAX;
1384 $bestMaskNum = 0;
1385 $bestMask = array();
1386 $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
1387 if (\QR_FIND_FROM_RANDOM !== \false) {
1388 $howManuOut = 8 - \QR_FIND_FROM_RANDOM % 9;
1389 for ($i = 0; $i < $howManuOut; ++$i) {
1390 $remPos = \rand(0, \count($checked_masks) - 1);
1391 unset($checked_masks[$remPos]);
1392 $checked_masks = \array_values($checked_masks);
1393 }
1394 }
1395 $bestMask = $frame;
1396 foreach ($checked_masks as $i) {
1397 $mask = \array_fill(0, $width, \str_repeat("\x00", $width));
1398 $demerit = 0;
1399 $blacks = 0;
1400 $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
1401 $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
1402 $blacks = (int) (100 * $blacks / ($width * $width));
1403 $demerit = (int) ((int) (\abs($blacks - 50) / 5) * \N4);
1404 $demerit += $this->evaluateSymbol($width, $mask);
1405 if ($demerit < $minDemerit) {
1406 $minDemerit = $demerit;
1407 $bestMask = $mask;
1408 $bestMaskNum = $i;
1409 }
1410 }
1411 return $bestMask;
1412 }
1413 // - - - - - - - - - - - - - - - - - - - - - - - - -
1414 // QRsplit
1415 /**
1416 * Return true if the character at specified position is a number
1417 * @param string $str string
1418 * @param int $pos characted position
1419 * @return boolean true of false
1420 */
1421 protected function isdigitat($str, $pos)
1422 {
1423 if ($pos >= \strlen($str)) {
1424 return \false;
1425 }
1426 return \ord($str[$pos]) >= \ord('0') && \ord($str[$pos]) <= \ord('9');
1427 }
1428 /**
1429 * Return true if the character at specified position is an alphanumeric character
1430 * @param string $str string
1431 * @param int $pos characted position
1432 * @return boolean true of false
1433 */
1434 protected function isalnumat($str, $pos)
1435 {
1436 if ($pos >= \strlen($str)) {
1437 return \false;
1438 }
1439 return $this->lookAnTable(\ord($str[$pos])) >= 0;
1440 }
1441 /**
1442 * identifyMode
1443 * @param int $pos
1444 * @return int mode
1445 */
1446 protected function identifyMode($pos)
1447 {
1448 if ($pos >= \strlen($this->dataStr)) {
1449 return \QR_MODE_NL;
1450 }
1451 $c = $this->dataStr[$pos];
1452 if ($this->isdigitat($this->dataStr, $pos)) {
1453 return \QR_MODE_NM;
1454 } elseif ($this->isalnumat($this->dataStr, $pos)) {
1455 return \QR_MODE_AN;
1456 } elseif ($this->hint == \QR_MODE_KJ) {
1457 if ($pos + 1 < \strlen($this->dataStr)) {
1458 $d = $this->dataStr[$pos + 1];
1459 $word = \ord($c) << 8 | \ord($d);
1460 if ($word >= 0x8140 && $word <= 0x9ffc or $word >= 0xe040 && $word <= 0xebbf) {
1461 return \QR_MODE_KJ;
1462 }
1463 }
1464 }
1465 return \QR_MODE_8B;
1466 }
1467 /**
1468 * eatNum
1469 * @return int run
1470 */
1471 protected function eatNum()
1472 {
1473 $ln = $this->lengthIndicator(\QR_MODE_NM, $this->version);
1474 $p = 0;
1475 while ($this->isdigitat($this->dataStr, $p)) {
1476 $p++;
1477 }
1478 $run = $p;
1479 $mode = $this->identifyMode($p);
1480 if ($mode == \QR_MODE_8B) {
1481 $dif = $this->estimateBitsModeNum($run) + 4 + $ln + $this->estimateBitsMode8(1) - $this->estimateBitsMode8($run + 1);
1482 // - 4 - l8
1483 if ($dif > 0) {
1484 return $this->eat8();
1485 }
1486 }
1487 if ($mode == \QR_MODE_AN) {
1488 $dif = $this->estimateBitsModeNum($run) + 4 + $ln + $this->estimateBitsModeAn(1) - $this->estimateBitsModeAn($run + 1);
1489 // - 4 - la
1490 if ($dif > 0) {
1491 return $this->eatAn();
1492 }
1493 }
1494 $this->items = $this->appendNewInputItem($this->items, \QR_MODE_NM, $run, \str_split($this->dataStr));
1495 return $run;
1496 }
1497 /**
1498 * eatAn
1499 * @return int run
1500 */
1501 protected function eatAn()
1502 {
1503 $la = $this->lengthIndicator(\QR_MODE_AN, $this->version);
1504 $ln = $this->lengthIndicator(\QR_MODE_NM, $this->version);
1505 $p = 1;
1506 while ($this->isalnumat($this->dataStr, $p)) {
1507 if ($this->isdigitat($this->dataStr, $p)) {
1508 $q = $p;
1509 while ($this->isdigitat($this->dataStr, $q)) {
1510 $q++;
1511 }
1512 $dif = $this->estimateBitsModeAn($p) + $this->estimateBitsModeNum($q - $p) + 4 + $ln - $this->estimateBitsModeAn($q);
1513 // - 4 - la
1514 if ($dif < 0) {
1515 break;
1516 } else {
1517 $p = $q;
1518 }
1519 } else {
1520 $p++;
1521 }
1522 }
1523 $run = $p;
1524 if (!$this->isalnumat($this->dataStr, $p)) {
1525 $dif = $this->estimateBitsModeAn($run) + 4 + $la + $this->estimateBitsMode8(1) - $this->estimateBitsMode8($run + 1);
1526 // - 4 - l8
1527 if ($dif > 0) {
1528 return $this->eat8();
1529 }
1530 }
1531 $this->items = $this->appendNewInputItem($this->items, \QR_MODE_AN, $run, \str_split($this->dataStr));
1532 return $run;
1533 }
1534 /**
1535 * eatKanji
1536 * @return int run
1537 */
1538 protected function eatKanji()
1539 {
1540 $p = 0;
1541 while ($this->identifyMode($p) == \QR_MODE_KJ) {
1542 $p += 2;
1543 }
1544 $this->items = $this->appendNewInputItem($this->items, \QR_MODE_KJ, $p, \str_split($this->dataStr));
1545 $run = $p;
1546 return $run;
1547 }
1548 /**
1549 * eat8
1550 * @return int run
1551 */
1552 protected function eat8()
1553 {
1554 $la = $this->lengthIndicator(\QR_MODE_AN, $this->version);
1555 $ln = $this->lengthIndicator(\QR_MODE_NM, $this->version);
1556 $p = 1;
1557 $dataStrLen = \strlen($this->dataStr);
1558 while ($p < $dataStrLen) {
1559 $mode = $this->identifyMode($p);
1560 if ($mode == \QR_MODE_KJ) {
1561 break;
1562 }
1563 if ($mode == \QR_MODE_NM) {
1564 $q = $p;
1565 while ($this->isdigitat($this->dataStr, $q)) {
1566 $q++;
1567 }
1568 $dif = $this->estimateBitsMode8($p) + $this->estimateBitsModeNum($q - $p) + 4 + $ln - $this->estimateBitsMode8($q);
1569 // - 4 - l8
1570 if ($dif < 0) {
1571 break;
1572 } else {
1573 $p = $q;
1574 }
1575 } elseif ($mode == \QR_MODE_AN) {
1576 $q = $p;
1577 while ($this->isalnumat($this->dataStr, $q)) {
1578 $q++;
1579 }
1580 $dif = $this->estimateBitsMode8($p) + $this->estimateBitsModeAn($q - $p) + 4 + $la - $this->estimateBitsMode8($q);
1581 // - 4 - l8
1582 if ($dif < 0) {
1583 break;
1584 } else {
1585 $p = $q;
1586 }
1587 } else {
1588 $p++;
1589 }
1590 }
1591 $run = $p;
1592 $this->items = $this->appendNewInputItem($this->items, \QR_MODE_8B, $run, \str_split($this->dataStr));
1593 return $run;
1594 }
1595 /**
1596 * splitString
1597 * @return int
1598 */
1599 protected function splitString()
1600 {
1601 while (\strlen($this->dataStr) > 0) {
1602 $mode = $this->identifyMode(0);
1603 switch ($mode) {
1604 case \QR_MODE_NM:
1605 $length = $this->eatNum();
1606 break;
1607 case \QR_MODE_AN:
1608 $length = $this->eatAn();
1609 break;
1610 case \QR_MODE_KJ:
1611 if ($this->hint == \QR_MODE_KJ) {
1612 $length = $this->eatKanji();
1613 } else {
1614 $length = $this->eat8();
1615 }
1616 break;
1617 default:
1618 $length = $this->eat8();
1619 break;
1620 }
1621 if ($length == 0) {
1622 return 0;
1623 }
1624 if ($length < 0) {
1625 return -1;
1626 }
1627 $this->dataStr = \substr($this->dataStr, $length);
1628 }
1629 return 0;
1630 }
1631 /**
1632 * toUpper
1633 */
1634 protected function toUpper()
1635 {
1636 $stringLen = \strlen($this->dataStr);
1637 $p = 0;
1638 while ($p < $stringLen) {
1639 $mode = $this->identifyMode(\substr($this->dataStr, $p));
1640 if ($mode == \QR_MODE_KJ) {
1641 $p += 2;
1642 } else {
1643 if (\ord($this->dataStr[$p]) >= \ord('a') and \ord($this->dataStr[$p]) <= \ord('z')) {
1644 $this->dataStr[$p] = \chr(\ord($this->dataStr[$p]) - 32);
1645 }
1646 $p++;
1647 }
1648 }
1649 return $this->dataStr;
1650 }
1651 // - - - - - - - - - - - - - - - - - - - - - - - - -
1652 // QRinputItem
1653 /**
1654 * newInputItem
1655 * @param int $mode
1656 * @param int $size
1657 * @param array $data
1658 * @param array $bstream
1659 * @return array input item
1660 */
1661 protected function newInputItem($mode, $size, $data, $bstream = null)
1662 {
1663 $setData = \array_slice($data, 0, $size);
1664 if (\count($setData) < $size) {
1665 $setData = \array_merge($setData, \array_fill(0, $size - \count($setData), 0));
1666 }
1667 if (!$this->check($mode, $size, $setData)) {
1668 return NULL;
1669 }
1670 $inputitem = array();
1671 $inputitem['mode'] = $mode;
1672 $inputitem['size'] = $size;
1673 $inputitem['data'] = $setData;
1674 $inputitem['bstream'] = $bstream;
1675 return $inputitem;
1676 }
1677 /**
1678 * encodeModeNum
1679 * @param array $inputitem
1680 * @param int $version
1681 * @return array input item
1682 */
1683 protected function encodeModeNum($inputitem, $version)
1684 {
1685 $words = (int) ($inputitem['size'] / 3);
1686 $inputitem['bstream'] = array();
1687 $val = 0x1;
1688 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1689 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(\QR_MODE_NM, $version), $inputitem['size']);
1690 for ($i = 0; $i < $words; ++$i) {
1691 $val = (\ord($inputitem['data'][$i * 3]) - \ord('0')) * 100;
1692 $val += (\ord($inputitem['data'][$i * 3 + 1]) - \ord('0')) * 10;
1693 $val += \ord($inputitem['data'][$i * 3 + 2]) - \ord('0');
1694 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
1695 }
1696 if ($inputitem['size'] - $words * 3 == 1) {
1697 $val = \ord($inputitem['data'][$words * 3]) - \ord('0');
1698 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1699 } elseif ($inputitem['size'] - $words * 3 == 2) {
1700 $val = (\ord($inputitem['data'][$words * 3]) - \ord('0')) * 10;
1701 $val += \ord($inputitem['data'][$words * 3 + 1]) - \ord('0');
1702 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
1703 }
1704 return $inputitem;
1705 }
1706 /**
1707 * encodeModeAn
1708 * @param array $inputitem
1709 * @param int $version
1710 * @return array input item
1711 */
1712 protected function encodeModeAn($inputitem, $version)
1713 {
1714 $words = (int) ($inputitem['size'] / 2);
1715 $inputitem['bstream'] = array();
1716 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x2);
1717 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(\QR_MODE_AN, $version), $inputitem['size']);
1718 for ($i = 0; $i < $words; ++$i) {
1719 $val = (int) ($this->lookAnTable(\ord($inputitem['data'][$i * 2])) * 45);
1720 $val += (int) $this->lookAnTable(\ord($inputitem['data'][$i * 2 + 1]));
1721 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
1722 }
1723 if ($inputitem['size'] & 1) {
1724 $val = $this->lookAnTable(\ord($inputitem['data'][$words * 2]));
1725 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
1726 }
1727 return $inputitem;
1728 }
1729 /**
1730 * encodeMode8
1731 * @param array $inputitem
1732 * @param int $version
1733 * @return array input item
1734 */
1735 protected function encodeMode8($inputitem, $version)
1736 {
1737 $inputitem['bstream'] = array();
1738 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
1739 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(\QR_MODE_8B, $version), $inputitem['size']);
1740 for ($i = 0; $i < $inputitem['size']; ++$i) {
1741 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, \ord($inputitem['data'][$i]));
1742 }
1743 return $inputitem;
1744 }
1745 /**
1746 * encodeModeKanji
1747 * @param array $inputitem
1748 * @param int $version
1749 * @return array input item
1750 */
1751 protected function encodeModeKanji($inputitem, $version)
1752 {
1753 $inputitem['bstream'] = array();
1754 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
1755 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(\QR_MODE_KJ, $version), (int) ($inputitem['size'] / 2));
1756 for ($i = 0; $i < $inputitem['size']; $i += 2) {
1757 $val = \ord($inputitem['data'][$i]) << 8 | \ord($inputitem['data'][$i + 1]);
1758 if ($val <= 0x9ffc) {
1759 $val -= 0x8140;
1760 } else {
1761 $val -= 0xc140;
1762 }
1763 $h = ($val >> 8) * 0xc0;
1764 $val = ($val & 0xff) + $h;
1765 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
1766 }
1767 return $inputitem;
1768 }
1769 /**
1770 * encodeModeStructure
1771 * @param array $inputitem
1772 * @return array input item
1773 */
1774 protected function encodeModeStructure($inputitem)
1775 {
1776 $inputitem['bstream'] = array();
1777 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x3);
1778 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, \ord($inputitem['data'][1]) - 1);
1779 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, \ord($inputitem['data'][0]) - 1);
1780 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, \ord($inputitem['data'][2]));
1781 return $inputitem;
1782 }
1783 /**
1784 * encodeBitStream
1785 * @param array $inputitem
1786 * @param int $version
1787 * @return array input item
1788 */
1789 protected function encodeBitStream($inputitem, $version)
1790 {
1791 $inputitem['bstream'] = array();
1792 $words = $this->maximumWords($inputitem['mode'], $version);
1793 if ($inputitem['size'] > $words) {
1794 $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
1795 $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, \array_slice($inputitem['data'], $words));
1796 $st1 = $this->encodeBitStream($st1, $version);
1797 $st2 = $this->encodeBitStream($st2, $version);
1798 $inputitem['bstream'] = array();
1799 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
1800 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
1801 } else {
1802 switch ($inputitem['mode']) {
1803 case \QR_MODE_NM:
1804 $inputitem = $this->encodeModeNum($inputitem, $version);
1805 break;
1806 case \QR_MODE_AN:
1807 $inputitem = $this->encodeModeAn($inputitem, $version);
1808 break;
1809 case \QR_MODE_8B:
1810 $inputitem = $this->encodeMode8($inputitem, $version);
1811 break;
1812 case \QR_MODE_KJ:
1813 $inputitem = $this->encodeModeKanji($inputitem, $version);
1814 break;
1815 case \QR_MODE_ST:
1816 $inputitem = $this->encodeModeStructure($inputitem);
1817 break;
1818 default:
1819 break;
1820 }
1821 }
1822 return $inputitem;
1823 }
1824 // - - - - - - - - - - - - - - - - - - - - - - - - -
1825 // QRinput
1826 /**
1827 * Append data to an input object.
1828 * The data is copied and appended to the input object.
1829 * @param array $items input items
1830 * @param int $mode encoding mode.
1831 * @param int $size size of data (byte).
1832 * @param array $data array of input data.
1833 * @return array items
1834 *
1835 */
1836 protected function appendNewInputItem($items, $mode, $size, $data)
1837 {
1838 $newitem = $this->newInputItem($mode, $size, $data);
1839 if (!empty($newitem)) {
1840 $items[] = $newitem;
1841 }
1842 return $items;
1843 }
1844 /**
1845 * insertStructuredAppendHeader
1846 * @param array $items
1847 * @param int $size
1848 * @param int $index
1849 * @param int $parity
1850 * @return array items
1851 */
1852 protected function insertStructuredAppendHeader($items, $size, $index, $parity)
1853 {
1854 if ($size > \MAX_STRUCTURED_SYMBOLS) {
1855 return -1;
1856 }
1857 if ($index <= 0 or $index > \MAX_STRUCTURED_SYMBOLS) {
1858 return -1;
1859 }
1860 $buf = array($size, $index, $parity);
1861 $entry = $this->newInputItem(\QR_MODE_ST, 3, $buf);
1862 \array_unshift($items, $entry);
1863 return $items;
1864 }
1865 /**
1866 * calcParity
1867 * @param array $items
1868 * @return int parity
1869 */
1870 protected function calcParity($items)
1871 {
1872 $parity = 0;
1873 foreach ($items as $item) {
1874 if ($item['mode'] != \QR_MODE_ST) {
1875 for ($i = $item['size'] - 1; $i >= 0; --$i) {
1876 $parity ^= $item['data'][$i];
1877 }
1878 }
1879 }
1880 return $parity;
1881 }
1882 /**
1883 * checkModeNum
1884 * @param int $size
1885 * @param array $data
1886 * @return boolean true or false
1887 */
1888 protected function checkModeNum($size, $data)
1889 {
1890 for ($i = 0; $i < $size; ++$i) {
1891 if (\ord($data[$i]) < \ord('0') or \ord($data[$i]) > \ord('9')) {
1892 return \false;
1893 }
1894 }
1895 return \true;
1896 }
1897 /**
1898 * Look up the alphabet-numeric conversion table (see JIS X0510:2004, pp.19).
1899 * @param int $c character value
1900 * @return int value
1901 */
1902 protected function lookAnTable($c)
1903 {
1904 return $c > 127 ? -1 : $this->anTable[$c];
1905 }
1906 /**
1907 * checkModeAn
1908 * @param int $size
1909 * @param array $data
1910 * @return boolean true or false
1911 */
1912 protected function checkModeAn($size, $data)
1913 {
1914 for ($i = 0; $i < $size; ++$i) {
1915 if ($this->lookAnTable(\ord($data[$i])) == -1) {
1916 return \false;
1917 }
1918 }
1919 return \true;
1920 }
1921 /**
1922 * estimateBitsModeNum
1923 * @param int $size
1924 * @return int number of bits
1925 */
1926 protected function estimateBitsModeNum($size)
1927 {
1928 $w = (int) ($size / 3);
1929 $bits = $w * 10;
1930 switch ($size - $w * 3) {
1931 case 1:
1932 $bits += 4;
1933 break;
1934 case 2:
1935 $bits += 7;
1936 break;
1937 }
1938 return $bits;
1939 }
1940 /**
1941 * estimateBitsModeAn
1942 * @param int $size
1943 * @return int number of bits
1944 */
1945 protected function estimateBitsModeAn($size)
1946 {
1947 $bits = (int) ($size * 5.5);
1948 // (size / 2 ) * 11
1949 if ($size & 1) {
1950 $bits += 6;
1951 }
1952 return $bits;
1953 }
1954 /**
1955 * estimateBitsMode8
1956 * @param int $size
1957 * @return int number of bits
1958 */
1959 protected function estimateBitsMode8($size)
1960 {
1961 return (int) ($size * 8);
1962 }
1963 /**
1964 * estimateBitsModeKanji
1965 * @param int $size
1966 * @return int number of bits
1967 */
1968 protected function estimateBitsModeKanji($size)
1969 {
1970 return (int) ($size * 6.5);
1971 // (size / 2 ) * 13
1972 }
1973 /**
1974 * checkModeKanji
1975 * @param int $size
1976 * @param array $data
1977 * @return boolean true or false
1978 */
1979 protected function checkModeKanji($size, $data)
1980 {
1981 if ($size & 1) {
1982 return \false;
1983 }
1984 for ($i = 0; $i < $size; $i += 2) {
1985 $val = \ord($data[$i]) << 8 | \ord($data[$i + 1]);
1986 if ($val < 0x8140 or $val > 0x9ffc and $val < 0xe040 or $val > 0xebbf) {
1987 return \false;
1988 }
1989 }
1990 return \true;
1991 }
1992 /**
1993 * Validate the input data.
1994 * @param int $mode encoding mode.
1995 * @param int $size size of data (byte).
1996 * @param array $data data to validate
1997 * @return boolean true in case of valid data, false otherwise
1998 */
1999 protected function check($mode, $size, $data)
2000 {
2001 if ($size <= 0) {
2002 return \false;
2003 }
2004 switch ($mode) {
2005 case \QR_MODE_NM:
2006 return $this->checkModeNum($size, $data);
2007 case \QR_MODE_AN:
2008 return $this->checkModeAn($size, $data);
2009 case \QR_MODE_KJ:
2010 return $this->checkModeKanji($size, $data);
2011 case \QR_MODE_8B:
2012 return \true;
2013 case \QR_MODE_ST:
2014 return \true;
2015 default:
2016 break;
2017 }
2018 return \false;
2019 }
2020 /**
2021 * estimateBitStreamSize
2022 * @param array $items
2023 * @param int $version
2024 * @return int bits
2025 */
2026 protected function estimateBitStreamSize($items, $version)
2027 {
2028 $bits = 0;
2029 if ($version == 0) {
2030 $version = 1;
2031 }
2032 foreach ($items as $item) {
2033 switch ($item['mode']) {
2034 case \QR_MODE_NM:
2035 $bits = $this->estimateBitsModeNum($item['size']);
2036 break;
2037 case \QR_MODE_AN:
2038 $bits = $this->estimateBitsModeAn($item['size']);
2039 break;
2040 case \QR_MODE_8B:
2041 $bits = $this->estimateBitsMode8($item['size']);
2042 break;
2043 case \QR_MODE_KJ:
2044 $bits = $this->estimateBitsModeKanji($item['size']);
2045 break;
2046 case \QR_MODE_ST:
2047 return \STRUCTURE_HEADER_BITS;
2048 default:
2049 return 0;
2050 }
2051 $l = $this->lengthIndicator($item['mode'], $version);
2052 $m = 1 << $l;
2053 $num = (int) (($item['size'] + $m - 1) / $m);
2054 $bits += $num * (4 + $l);
2055 }
2056 return $bits;
2057 }
2058 /**
2059 * estimateVersion
2060 * @param array $items
2061 * @return int version
2062 */
2063 protected function estimateVersion($items)
2064 {
2065 $version = 0;
2066 $prev = 0;
2067 do {
2068 $prev = $version;
2069 $bits = $this->estimateBitStreamSize($items, $prev);
2070 $version = $this->getMinimumVersion((int) (($bits + 7) / 8), $this->level);
2071 if ($version < 0) {
2072 return -1;
2073 }
2074 } while ($version > $prev);
2075 return $version;
2076 }
2077 /**
2078 * lengthOfCode
2079 * @param int $mode
2080 * @param int $version
2081 * @param int $bits
2082 * @return int size
2083 */
2084 protected function lengthOfCode($mode, $version, $bits)
2085 {
2086 $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
2087 switch ($mode) {
2088 case \QR_MODE_NM:
2089 $chunks = (int) ($payload / 10);
2090 $remain = $payload - $chunks * 10;
2091 $size = $chunks * 3;
2092 if ($remain >= 7) {
2093 $size += 2;
2094 } elseif ($remain >= 4) {
2095 $size += 1;
2096 }
2097 break;
2098 case \QR_MODE_AN:
2099 $chunks = (int) ($payload / 11);
2100 $remain = $payload - $chunks * 11;
2101 $size = $chunks * 2;
2102 if ($remain >= 6) {
2103 ++$size;
2104 }
2105 break;
2106 case \QR_MODE_8B:
2107 $size = (int) ($payload / 8);
2108 break;
2109 case \QR_MODE_KJ:
2110 $size = (int) ($payload / 13 * 2);
2111 break;
2112 case \QR_MODE_ST:
2113 $size = (int) ($payload / 8);
2114 break;
2115 default:
2116 $size = 0;
2117 break;
2118 }
2119 $maxsize = $this->maximumWords($mode, $version);
2120 if ($size < 0) {
2121 $size = 0;
2122 }
2123 if ($size > $maxsize) {
2124 $size = $maxsize;
2125 }
2126 return $size;
2127 }
2128 /**
2129 * createBitStream
2130 * @param array $items
2131 * @return array of items and total bits
2132 */
2133 protected function createBitStream($items)
2134 {
2135 $total = 0;
2136 foreach ($items as $key => $item) {
2137 $items[$key] = $this->encodeBitStream($item, $this->version);
2138 $bits = \count($items[$key]['bstream']);
2139 $total += $bits;
2140 }
2141 return array($items, $total);
2142 }
2143 /**
2144 * convertData
2145 * @param array $items
2146 * @return array items
2147 */
2148 protected function convertData($items)
2149 {
2150 $ver = $this->estimateVersion($items);
2151 if ($ver > $this->version) {
2152 $this->version = $ver;
2153 }
2154 while (\true) {
2155 $cbs = $this->createBitStream($items);
2156 $items = $cbs[0];
2157 $bits = $cbs[1];
2158 if ($bits < 0) {
2159 return -1;
2160 }
2161 $ver = $this->getMinimumVersion((int) (($bits + 7) / 8), $this->level);
2162 if ($ver < 0) {
2163 return -1;
2164 } elseif ($ver > $this->version) {
2165 $this->version = $ver;
2166 } else {
2167 break;
2168 }
2169 }
2170 return $items;
2171 }
2172 /**
2173 * Append Padding Bit to bitstream
2174 * @param array $bstream
2175 * @return array bitstream
2176 */
2177 protected function appendPaddingBit($bstream)
2178 {
2179 if (\is_null($bstream)) {
2180 return null;
2181 }
2182 $bits = \count($bstream);
2183 $maxwords = $this->getDataLength($this->version, $this->level);
2184 $maxbits = $maxwords * 8;
2185 if ($maxbits == $bits) {
2186 return $bstream;
2187 }
2188 if ($maxbits - $bits < 5) {
2189 return $this->appendNum($bstream, $maxbits - $bits, 0);
2190 }
2191 $bits += 4;
2192 $words = (int) (($bits + 7) / 8);
2193 $padding = array();
2194 $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
2195 $padlen = $maxwords - $words;
2196 if ($padlen > 0) {
2197 $padbuf = array();
2198 for ($i = 0; $i < $padlen; ++$i) {
2199 $padbuf[$i] = $i & 1 ? 0x11 : 0xec;
2200 }
2201 $padding = $this->appendBytes($padding, $padlen, $padbuf);
2202 }
2203 return $this->appendBitstream($bstream, $padding);
2204 }
2205 /**
2206 * mergeBitStream
2207 * @param array $items items
2208 * @return array bitstream
2209 */
2210 protected function mergeBitStream($items)
2211 {
2212 $items = $this->convertData($items);
2213 if (!\is_array($items)) {
2214 return null;
2215 }
2216 $bstream = array();
2217 foreach ($items as $item) {
2218 $bstream = $this->appendBitstream($bstream, $item['bstream']);
2219 }
2220 return $bstream;
2221 }
2222 /**
2223 * Returns a stream of bits.
2224 * @param int $items
2225 * @return array padded merged byte stream
2226 */
2227 protected function getBitStream($items)
2228 {
2229 $bstream = $this->mergeBitStream($items);
2230 return $this->appendPaddingBit($bstream);
2231 }
2232 /**
2233 * Pack all bit streams padding bits into a byte array.
2234 * @param int $items
2235 * @return array padded merged byte stream
2236 */
2237 protected function getByteStream($items)
2238 {
2239 $bstream = $this->getBitStream($items);
2240 return $this->bitstreamToByte($bstream);
2241 }
2242 // - - - - - - - - - - - - - - - - - - - - - - - - -
2243 // QRbitstream
2244 /**
2245 * Return an array with zeros
2246 * @param int $setLength array size
2247 * @return array
2248 */
2249 protected function allocate($setLength)
2250 {
2251 return \array_fill(0, $setLength, 0);
2252 }
2253 /**
2254 * Return new bitstream from number
2255 * @param int $bits number of bits
2256 * @param int $num number
2257 * @return array bitstream
2258 */
2259 protected function newFromNum($bits, $num)
2260 {
2261 $bstream = $this->allocate($bits);
2262 $mask = 1 << $bits - 1;
2263 for ($i = 0; $i < $bits; ++$i) {
2264 if ($num & $mask) {
2265 $bstream[$i] = 1;
2266 } else {
2267 $bstream[$i] = 0;
2268 }
2269 $mask = $mask >> 1;
2270 }
2271 return $bstream;
2272 }
2273 /**
2274 * Return new bitstream from bytes
2275 * @param int $size size
2276 * @param array $data bytes
2277 * @return array bitstream
2278 */
2279 protected function newFromBytes($size, $data)
2280 {
2281 $bstream = $this->allocate($size * 8);
2282 $p = 0;
2283 for ($i = 0; $i < $size; ++$i) {
2284 $mask = 0x80;
2285 for ($j = 0; $j < 8; ++$j) {
2286 if ($data[$i] & $mask) {
2287 $bstream[$p] = 1;
2288 } else {
2289 $bstream[$p] = 0;
2290 }
2291 $p++;
2292 $mask = $mask >> 1;
2293 }
2294 }
2295 return $bstream;
2296 }
2297 /**
2298 * Append one bitstream to another
2299 * @param array $bitstream original bitstream
2300 * @param array $append bitstream to append
2301 * @return array bitstream
2302 */
2303 protected function appendBitstream($bitstream, $append)
2304 {
2305 if (!\is_array($append) or \count($append) == 0) {
2306 return $bitstream;
2307 }
2308 if (\count($bitstream) == 0) {
2309 return $append;
2310 }
2311 return \array_values(\array_merge($bitstream, $append));
2312 }
2313 /**
2314 * Append one bitstream created from number to another
2315 * @param array $bitstream original bitstream
2316 * @param int $bits number of bits
2317 * @param int $num number
2318 * @return array bitstream
2319 */
2320 protected function appendNum($bitstream, $bits, $num)
2321 {
2322 if ($bits == 0) {
2323 return 0;
2324 }
2325 $b = $this->newFromNum($bits, $num);
2326 return $this->appendBitstream($bitstream, $b);
2327 }
2328 /**
2329 * Append one bitstream created from bytes to another
2330 * @param array $bitstream original bitstream
2331 * @param int $size size
2332 * @param array $data bytes
2333 * @return array bitstream
2334 */
2335 protected function appendBytes($bitstream, $size, $data)
2336 {
2337 if ($size == 0) {
2338 return 0;
2339 }
2340 $b = $this->newFromBytes($size, $data);
2341 return $this->appendBitstream($bitstream, $b);
2342 }
2343 /**
2344 * Convert bitstream to bytes
2345 * @param array $bstream original bitstream
2346 * @return array of bytes
2347 */
2348 protected function bitstreamToByte($bstream)
2349 {
2350 if (\is_null($bstream)) {
2351 return null;
2352 }
2353 $size = \count($bstream);
2354 if ($size == 0) {
2355 return array();
2356 }
2357 $data = \array_fill(0, (int) (($size + 7) / 8), 0);
2358 $bytes = (int) ($size / 8);
2359 $p = 0;
2360 for ($i = 0; $i < $bytes; $i++) {
2361 $v = 0;
2362 for ($j = 0; $j < 8; $j++) {
2363 $v = $v << 1;
2364 $v |= $bstream[$p];
2365 $p++;
2366 }
2367 $data[$i] = $v;
2368 }
2369 if ($size & 7) {
2370 $v = 0;
2371 for ($j = 0; $j < ($size & 7); $j++) {
2372 $v = $v << 1;
2373 $v |= $bstream[$p];
2374 $p++;
2375 }
2376 $data[$bytes] = $v;
2377 }
2378 return $data;
2379 }
2380 // - - - - - - - - - - - - - - - - - - - - - - - - -
2381 // QRspec
2382 /**
2383 * Replace a value on the array at the specified position
2384 * @param array $srctab
2385 * @param int $x X position
2386 * @param int $y Y position
2387 * @param string $repl value to replace
2388 * @param int $replLen length of the repl string
2389 * @return array srctab
2390 */
2391 protected function qrstrset($srctab, $x, $y, $repl, $replLen = \false)
2392 {
2393 $srctab[$y] = \substr_replace($srctab[$y], $replLen !== \false ? \substr($repl, 0, $replLen) : $repl, $x, $replLen !== \false ? $replLen : \strlen($repl));
2394 return $srctab;
2395 }
2396 /**
2397 * Return maximum data code length (bytes) for the version.
2398 * @param int $version version
2399 * @param int $level error correction level
2400 * @return int maximum size (bytes)
2401 */
2402 protected function getDataLength($version, $level)
2403 {
2404 return $this->capacity[$version][\QRCAP_WORDS] - $this->capacity[$version][\QRCAP_EC][$level];
2405 }
2406 /**
2407 * Return maximum error correction code length (bytes) for the version.
2408 * @param int $version version
2409 * @param int $level error correction level
2410 * @return int ECC size (bytes)
2411 */
2412 protected function getECCLength($version, $level)
2413 {
2414 return $this->capacity[$version][\QRCAP_EC][$level];
2415 }
2416 /**
2417 * Return the width of the symbol for the version.
2418 * @param int $version version
2419 * @return int width
2420 */
2421 protected function getWidth($version)
2422 {
2423 return $this->capacity[$version][\QRCAP_WIDTH];
2424 }
2425 /**
2426 * Return the numer of remainder bits.
2427 * @param int $version version
2428 * @return int number of remainder bits
2429 */
2430 protected function getRemainder($version)
2431 {
2432 return $this->capacity[$version][\QRCAP_REMINDER];
2433 }
2434 /**
2435 * Return a version number that satisfies the input code length.
2436 * @param int $size input code length (bytes)
2437 * @param int $level error correction level
2438 * @return int version number
2439 */
2440 protected function getMinimumVersion($size, $level)
2441 {
2442 for ($i = 1; $i <= \QRSPEC_VERSION_MAX; ++$i) {
2443 $words = $this->capacity[$i][\QRCAP_WORDS] - $this->capacity[$i][\QRCAP_EC][$level];
2444 if ($words >= $size) {
2445 return $i;
2446 }
2447 }
2448 // the size of input data is greater than QR capacity, try to lover the error correction mode
2449 return -1;
2450 }
2451 /**
2452 * Return the size of length indicator for the mode and version.
2453 * @param int $mode encoding mode
2454 * @param int $version version
2455 * @return int the size of the appropriate length indicator (bits).
2456 */
2457 protected function lengthIndicator($mode, $version)
2458 {
2459 if ($mode == \QR_MODE_ST) {
2460 return 0;
2461 }
2462 if ($version <= 9) {
2463 $l = 0;
2464 } elseif ($version <= 26) {
2465 $l = 1;
2466 } else {
2467 $l = 2;
2468 }
2469 return $this->lengthTableBits[$mode][$l];
2470 }
2471 /**
2472 * Return the maximum length for the mode and version.
2473 * @param int $mode encoding mode
2474 * @param int $version version
2475 * @return int the maximum length (bytes)
2476 */
2477 protected function maximumWords($mode, $version)
2478 {
2479 if ($mode == \QR_MODE_ST) {
2480 return 3;
2481 }
2482 if ($version <= 9) {
2483 $l = 0;
2484 } else {
2485 if ($version <= 26) {
2486 $l = 1;
2487 } else {
2488 $l = 2;
2489 }
2490 }
2491 $bits = $this->lengthTableBits[$mode][$l];
2492 $words = (1 << $bits) - 1;
2493 if ($mode == \QR_MODE_KJ) {
2494 $words *= 2;
2495 // the number of bytes is required
2496 }
2497 return $words;
2498 }
2499 /**
2500 * Return an array of ECC specification.
2501 * @param int $version version
2502 * @param int $level error correction level
2503 * @param array $spec an array of ECC specification contains as following: {# of type1 blocks, # of data code, # of ecc code, # of type2 blocks, # of data code}
2504 * @return array spec
2505 */
2506 protected function getEccSpec($version, $level, $spec)
2507 {
2508 if (\count($spec) < 5) {
2509 $spec = array(0, 0, 0, 0, 0);
2510 }
2511 $b1 = $this->eccTable[$version][$level][0];
2512 $b2 = $this->eccTable[$version][$level][1];
2513 $data = $this->getDataLength($version, $level);
2514 $ecc = $this->getECCLength($version, $level);
2515 if ($b2 == 0) {
2516 $spec[0] = $b1;
2517 $spec[1] = (int) ($data / $b1);
2518 $spec[2] = (int) ($ecc / $b1);
2519 $spec[3] = 0;
2520 $spec[4] = 0;
2521 } else {
2522 $spec[0] = $b1;
2523 $spec[1] = (int) ($data / ($b1 + $b2));
2524 $spec[2] = (int) ($ecc / ($b1 + $b2));
2525 $spec[3] = $b2;
2526 $spec[4] = $spec[1] + 1;
2527 }
2528 return $spec;
2529 }
2530 /**
2531 * Put an alignment marker.
2532 * @param array $frame frame
2533 * @param int $ox X center coordinate of the pattern
2534 * @param int $oy Y center coordinate of the pattern
2535 * @return array frame
2536 */
2537 protected function putAlignmentMarker($frame, $ox, $oy)
2538 {
2539 $finder = array("\xa1\xa1\xa1\xa1\xa1", "\xa1\xa0\xa0\xa0\xa1", "\xa1\xa0\xa1\xa0\xa1", "\xa1\xa0\xa0\xa0\xa1", "\xa1\xa1\xa1\xa1\xa1");
2540 $yStart = $oy - 2;
2541 $xStart = $ox - 2;
2542 for ($y = 0; $y < 5; $y++) {
2543 $frame = $this->qrstrset($frame, $xStart, $yStart + $y, $finder[$y]);
2544 }
2545 return $frame;
2546 }
2547 /**
2548 * Put an alignment pattern.
2549 * @param int $version version
2550 * @param array $frame frame
2551 * @param int $width width
2552 * @return array frame
2553 */
2554 protected function putAlignmentPattern($version, $frame, $width)
2555 {
2556 if ($version < 2) {
2557 return $frame;
2558 }
2559 $d = $this->alignmentPattern[$version][1] - $this->alignmentPattern[$version][0];
2560 if ($d < 0) {
2561 $w = 2;
2562 } else {
2563 $w = (int) (($width - $this->alignmentPattern[$version][0]) / $d + 2);
2564 }
2565 if ($w * $w - 3 == 1) {
2566 $x = $this->alignmentPattern[$version][0];
2567 $y = $this->alignmentPattern[$version][0];
2568 $frame = $this->putAlignmentMarker($frame, $x, $y);
2569 return $frame;
2570 }
2571 $cx = $this->alignmentPattern[$version][0];
2572 $wo = $w - 1;
2573 for ($x = 1; $x < $wo; ++$x) {
2574 $frame = $this->putAlignmentMarker($frame, 6, $cx);
2575 $frame = $this->putAlignmentMarker($frame, $cx, 6);
2576 $cx += $d;
2577 }
2578 $cy = $this->alignmentPattern[$version][0];
2579 for ($y = 0; $y < $wo; ++$y) {
2580 $cx = $this->alignmentPattern[$version][0];
2581 for ($x = 0; $x < $wo; ++$x) {
2582 $frame = $this->putAlignmentMarker($frame, $cx, $cy);
2583 $cx += $d;
2584 }
2585 $cy += $d;
2586 }
2587 return $frame;
2588 }
2589 /**
2590 * Return BCH encoded version information pattern that is used for the symbol of version 7 or greater. Use lower 18 bits.
2591 * @param int $version version
2592 * @return string BCH encoded version information pattern
2593 */
2594 protected function getVersionPattern($version)
2595 {
2596 if ($version < 7 or $version > \QRSPEC_VERSION_MAX) {
2597 return 0;
2598 }
2599 return $this->versionPattern[$version - 7];
2600 }
2601 /**
2602 * Return BCH encoded format information pattern.
2603 * @param array $mask
2604 * @param int $level error correction level
2605 * @return string BCH encoded format information pattern
2606 */
2607 protected function getFormatInfo($mask, $level)
2608 {
2609 if ($mask < 0 or $mask > 7) {
2610 return 0;
2611 }
2612 if ($level < 0 or $level > 3) {
2613 return 0;
2614 }
2615 return $this->formatInfo[$level][$mask];
2616 }
2617 /**
2618 * Put a finder pattern.
2619 * @param array $frame frame
2620 * @param int $ox X center coordinate of the pattern
2621 * @param int $oy Y center coordinate of the pattern
2622 * @return array frame
2623 */
2624 protected function putFinderPattern($frame, $ox, $oy)
2625 {
2626 $finder = array("\xc1\xc1\xc1\xc1\xc1\xc1\xc1", "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", "\xc1\xc1\xc1\xc1\xc1\xc1\xc1");
2627 for ($y = 0; $y < 7; $y++) {
2628 $frame = $this->qrstrset($frame, $ox, $oy + $y, $finder[$y]);
2629 }
2630 return $frame;
2631 }
2632 /**
2633 * Return a copy of initialized frame.
2634 * @param int $version version
2635 * @return array array of unsigned char.
2636 */
2637 protected function createFrame($version)
2638 {
2639 $width = $this->capacity[$version][\QRCAP_WIDTH];
2640 $frameLine = \str_repeat("\x00", $width);
2641 $frame = \array_fill(0, $width, $frameLine);
2642 // Finder pattern
2643 $frame = $this->putFinderPattern($frame, 0, 0);
2644 $frame = $this->putFinderPattern($frame, $width - 7, 0);
2645 $frame = $this->putFinderPattern($frame, 0, $width - 7);
2646 // Separator
2647 $yOffset = $width - 7;
2648 for ($y = 0; $y < 7; ++$y) {
2649 $frame[$y][7] = "\xc0";
2650 $frame[$y][$width - 8] = "\xc0";
2651 $frame[$yOffset][7] = "\xc0";
2652 ++$yOffset;
2653 }
2654 $setPattern = \str_repeat("\xc0", 8);
2655 $frame = $this->qrstrset($frame, 0, 7, $setPattern);
2656 $frame = $this->qrstrset($frame, $width - 8, 7, $setPattern);
2657 $frame = $this->qrstrset($frame, 0, $width - 8, $setPattern);
2658 // Format info
2659 $setPattern = \str_repeat("\x84", 9);
2660 $frame = $this->qrstrset($frame, 0, 8, $setPattern);
2661 $frame = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8);
2662 $yOffset = $width - 8;
2663 for ($y = 0; $y < 8; ++$y, ++$yOffset) {
2664 $frame[$y][8] = "\x84";
2665 $frame[$yOffset][8] = "\x84";
2666 }
2667 // Timing pattern
2668 $wo = $width - 15;
2669 for ($i = 1; $i < $wo; ++$i) {
2670 $frame[6][7 + $i] = \chr(0x90 | $i & 1);
2671 $frame[7 + $i][6] = \chr(0x90 | $i & 1);
2672 }
2673 // Alignment pattern
2674 $frame = $this->putAlignmentPattern($version, $frame, $width);
2675 // Version information
2676 if ($version >= 7) {
2677 $vinf = $this->getVersionPattern($version);
2678 $v = $vinf;
2679 for ($x = 0; $x < 6; ++$x) {
2680 for ($y = 0; $y < 3; ++$y) {
2681 $frame[$width - 11 + $y][$x] = \chr(0x88 | $v & 1);
2682 $v = $v >> 1;
2683 }
2684 }
2685 $v = $vinf;
2686 for ($y = 0; $y < 6; ++$y) {
2687 for ($x = 0; $x < 3; ++$x) {
2688 $frame[$y][$x + ($width - 11)] = \chr(0x88 | $v & 1);
2689 $v = $v >> 1;
2690 }
2691 }
2692 }
2693 // and a little bit...
2694 $frame[$width - 8][8] = "\x81";
2695 return $frame;
2696 }
2697 /**
2698 * Set new frame for the specified version.
2699 * @param int $version version
2700 * @return array array of unsigned char.
2701 */
2702 protected function newFrame($version)
2703 {
2704 if ($version < 1 or $version > \QRSPEC_VERSION_MAX) {
2705 return NULL;
2706 }
2707 if (!isset($this->frames[$version])) {
2708 $this->frames[$version] = $this->createFrame($version);
2709 }
2710 if (\is_null($this->frames[$version])) {
2711 return NULL;
2712 }
2713 return $this->frames[$version];
2714 }
2715 /**
2716 * Return block number 0
2717 * @param array $spec
2718 * @return int value
2719 */
2720 protected function rsBlockNum($spec)
2721 {
2722 return $spec[0] + $spec[3];
2723 }
2724 /**
2725 * Return block number 1
2726 * @param array $spec
2727 * @return int value
2728 */
2729 protected function rsBlockNum1($spec)
2730 {
2731 return $spec[0];
2732 }
2733 /**
2734 * Return data codes 1
2735 * @param array $spec
2736 * @return int value
2737 */
2738 protected function rsDataCodes1($spec)
2739 {
2740 return $spec[1];
2741 }
2742 /**
2743 * Return ecc codes 1
2744 * @param array $spec
2745 * @return int value
2746 */
2747 protected function rsEccCodes1($spec)
2748 {
2749 return $spec[2];
2750 }
2751 /**
2752 * Return block number 2
2753 * @param array $spec
2754 * @return int value
2755 */
2756 protected function rsBlockNum2($spec)
2757 {
2758 return $spec[3];
2759 }
2760 /**
2761 * Return data codes 2
2762 * @param array $spec
2763 * @return int value
2764 */
2765 protected function rsDataCodes2($spec)
2766 {
2767 return $spec[4];
2768 }
2769 /**
2770 * Return ecc codes 2
2771 * @param array $spec
2772 * @return int value
2773 */
2774 protected function rsEccCodes2($spec)
2775 {
2776 return $spec[2];
2777 }
2778 /**
2779 * Return data length
2780 * @param array $spec
2781 * @return int value
2782 */
2783 protected function rsDataLength($spec)
2784 {
2785 return $spec[0] * $spec[1] + $spec[3] * $spec[4];
2786 }
2787 /**
2788 * Return ecc length
2789 * @param array $spec
2790 * @return int value
2791 */
2792 protected function rsEccLength($spec)
2793 {
2794 return ($spec[0] + $spec[3]) * $spec[2];
2795 }
2796 // - - - - - - - - - - - - - - - - - - - - - - - - -
2797 // QRrs
2798 /**
2799 * Initialize a Reed-Solomon codec and add it to existing rsitems
2800 * @param int $symsize symbol size, bits
2801 * @param int $gfpoly Field generator polynomial coefficients
2802 * @param int $fcr first root of RS code generator polynomial, index form
2803 * @param int $prim primitive element to generate polynomial roots
2804 * @param int $nroots RS code generator polynomial degree (number of roots)
2805 * @param int $pad padding bytes at front of shortened block
2806 * @return array Array of RS values:<ul><li>mm = Bits per symbol;</li><li>nn = Symbols per block;</li><li>alpha_to = log lookup table array;</li><li>index_of = Antilog lookup table array;</li><li>genpoly = Generator polynomial array;</li><li>nroots = Number of generator;</li><li>roots = number of parity symbols;</li><li>fcr = First consecutive root, index form;</li><li>prim = Primitive element, index form;</li><li>iprim = prim-th root of 1, index form;</li><li>pad = Padding bytes in shortened block;</li><li>gfpoly</ul>.
2807 */
2808 protected function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
2809 {
2810 foreach ($this->rsitems as $rs) {
2811 if ($rs['pad'] != $pad or $rs['nroots'] != $nroots or $rs['mm'] != $symsize or $rs['gfpoly'] != $gfpoly or $rs['fcr'] != $fcr or $rs['prim'] != $prim) {
2812 continue;
2813 }
2814 return $rs;
2815 }
2816 $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
2817 \array_unshift($this->rsitems, $rs);
2818 return $rs;
2819 }
2820 // - - - - - - - - - - - - - - - - - - - - - - - - -
2821 // QRrsItem
2822 /**
2823 * modnn
2824 * @param array $rs RS values
2825 * @param int $x X position
2826 * @return int X osition
2827 */
2828 protected function modnn($rs, $x)
2829 {
2830 while ($x >= $rs['nn']) {
2831 $x -= $rs['nn'];
2832 $x = ($x >> $rs['mm']) + ($x & $rs['nn']);
2833 }
2834 return $x;
2835 }
2836 /**
2837 * Initialize a Reed-Solomon codec and returns an array of values.
2838 * @param int $symsize symbol size, bits
2839 * @param int $gfpoly Field generator polynomial coefficients
2840 * @param int $fcr first root of RS code generator polynomial, index form
2841 * @param int $prim primitive element to generate polynomial roots
2842 * @param int $nroots RS code generator polynomial degree (number of roots)
2843 * @param int $pad padding bytes at front of shortened block
2844 * @return array Array of RS values:<ul><li>mm = Bits per symbol;</li><li>nn = Symbols per block;</li><li>alpha_to = log lookup table array;</li><li>index_of = Antilog lookup table array;</li><li>genpoly = Generator polynomial array;</li><li>nroots = Number of generator;</li><li>roots = number of parity symbols;</li><li>fcr = First consecutive root, index form;</li><li>prim = Primitive element, index form;</li><li>iprim = prim-th root of 1, index form;</li><li>pad = Padding bytes in shortened block;</li><li>gfpoly</ul>.
2845 */
2846 protected function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
2847 {
2848 // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2)
2849 $rs = null;
2850 // Check parameter ranges
2851 if ($symsize < 0 or $symsize > 8) {
2852 return $rs;
2853 }
2854 if ($fcr < 0 or $fcr >= 1 << $symsize) {
2855 return $rs;
2856 }
2857 if ($prim <= 0 or $prim >= 1 << $symsize) {
2858 return $rs;
2859 }
2860 if ($nroots < 0 or $nroots >= 1 << $symsize) {
2861 return $rs;
2862 }
2863 if ($pad < 0 or $pad >= (1 << $symsize) - 1 - $nroots) {
2864 return $rs;
2865 }
2866 $rs = array();
2867 $rs['mm'] = $symsize;
2868 $rs['nn'] = (1 << $symsize) - 1;
2869 $rs['pad'] = $pad;
2870 $rs['alpha_to'] = \array_fill(0, $rs['nn'] + 1, 0);
2871 $rs['index_of'] = \array_fill(0, $rs['nn'] + 1, 0);
2872 // PHP style macro replacement ;)
2873 $NN =& $rs['nn'];
2874 $A0 =& $NN;
2875 // Generate Galois field lookup tables
2876 $rs['index_of'][0] = $A0;
2877 // log(zero) = -inf
2878 $rs['alpha_to'][$A0] = 0;
2879 // alpha**-inf = 0
2880 $sr = 1;
2881 for ($i = 0; $i < $rs['nn']; ++$i) {
2882 $rs['index_of'][$sr] = $i;
2883 $rs['alpha_to'][$i] = $sr;
2884 $sr <<= 1;
2885 if ($sr & 1 << $symsize) {
2886 $sr ^= $gfpoly;
2887 }
2888 $sr &= $rs['nn'];
2889 }
2890 if ($sr != 1) {
2891 // field generator polynomial is not primitive!
2892 return NULL;
2893 }
2894 // Form RS code generator polynomial from its roots
2895 $rs['genpoly'] = \array_fill(0, $nroots + 1, 0);
2896 $rs['fcr'] = $fcr;
2897 $rs['prim'] = $prim;
2898 $rs['nroots'] = $nroots;
2899 $rs['gfpoly'] = $gfpoly;
2900 // Find prim-th root of 1, used in decoding
2901 for ($iprim = 1; $iprim % $prim != 0; $iprim += $rs['nn']) {
2902 // intentional empty-body loop!
2903 }
2904 $rs['iprim'] = (int) ($iprim / $prim);
2905 $rs['genpoly'][0] = 1;
2906 for ($i = 0, $root = $fcr * $prim; $i < $nroots; $i++, $root += $prim) {
2907 $rs['genpoly'][$i + 1] = 1;
2908 // Multiply rs->genpoly[] by @**(root + x)
2909 for ($j = $i; $j > 0; --$j) {
2910 if ($rs['genpoly'][$j] != 0) {
2911 $rs['genpoly'][$j] = $rs['genpoly'][$j - 1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)];
2912 } else {
2913 $rs['genpoly'][$j] = $rs['genpoly'][$j - 1];
2914 }
2915 }
2916 // rs->genpoly[0] can never be zero
2917 $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)];
2918 }
2919 // convert rs->genpoly[] to index form for quicker encoding
2920 for ($i = 0; $i <= $nroots; ++$i) {
2921 $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]];
2922 }
2923 return $rs;
2924 }
2925 /**
2926 * Encode a Reed-Solomon codec and returns the parity array
2927 * @param array $rs RS values
2928 * @param array $data data
2929 * @param array $parity parity
2930 * @return parity array
2931 */
2932 protected function encode_rs_char($rs, $data, $parity)
2933 {
2934 $MM =& $rs['mm'];
2935 // bits per symbol
2936 $NN =& $rs['nn'];
2937 // the total number of symbols in a RS block
2938 $ALPHA_TO =& $rs['alpha_to'];
2939 // the address of an array of NN elements to convert Galois field elements in index (log) form to polynomial form
2940 $INDEX_OF =& $rs['index_of'];
2941 // the address of an array of NN elements to convert Galois field elements in polynomial form to index (log) form
2942 $GENPOLY =& $rs['genpoly'];
2943 // an array of NROOTS+1 elements containing the generator polynomial in index form
2944 $NROOTS =& $rs['nroots'];
2945 // the number of roots in the RS code generator polynomial, which is the same as the number of parity symbols in a block
2946 $FCR =& $rs['fcr'];
2947 // first consecutive root, index form
2948 $PRIM =& $rs['prim'];
2949 // primitive element, index form
2950 $IPRIM =& $rs['iprim'];
2951 // prim-th root of 1, index form
2952 $PAD =& $rs['pad'];
2953 // the number of pad symbols in a block
2954 $A0 =& $NN;
2955 $parity = \array_fill(0, $NROOTS, 0);
2956 for ($i = 0; $i < $NN - $NROOTS - $PAD; $i++) {
2957 $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
2958 if ($feedback != $A0) {
2959 // feedback term is non-zero
2960 // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
2961 // always be for the polynomials constructed by init_rs()
2962 $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback);
2963 for ($j = 1; $j < $NROOTS; ++$j) {
2964 $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[$NROOTS - $j])];
2965 }
2966 }
2967 // Shift
2968 \array_shift($parity);
2969 if ($feedback != $A0) {
2970 \array_push($parity, $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]);
2971 } else {
2972 \array_push($parity, 0);
2973 }
2974 }
2975 return $parity;
2976 }
2977 }
2978 // end QRcode class
2979 //============================================================+
2980 // END OF FILE
2981 //============================================================+
2982 }
2983