PluginProbe
Welcart e-Commerce / 1.4.1
Welcart e-Commerce v1.4.1
2.12.4 2.12.3 2.11.35 2.12.2 2.12.1 2.11.34 2.11.33 2.11.32 2.11.31 2.11.30 1.3.16 1.3.17 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 All 292 releases
usc-e-shop / pdf / tcpdf / qrcode.php

qrcode.php in Welcart e-Commerce 1.4.1, at pdf/tcpdf/qrcode.php

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