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