OSDN Git Service

初回コミット(v2.6.17.1)
[magic3/magic3.git] / include / lib / tcpdf / qrcode.php
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 - 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.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
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.010
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          * @return (int)
1443          */
1444          protected function splitString() {
1445                 while (strlen($this->dataStr) > 0) {
1446                         $mode = $this->identifyMode(0);
1447                         switch ($mode) {
1448                                 case QR_MODE_NM: {
1449                                         $length = $this->eatNum();
1450                                         break;
1451                                 }
1452                                 case QR_MODE_AN: {
1453                                         $length = $this->eatAn();
1454                                         break;
1455                                 }
1456                                 case QR_MODE_KJ: {
1457                                         if ($hint == QR_MODE_KJ) {
1458                                                 $length = $this->eatKanji();
1459                                         } else {
1460                                                 $length = $this->eat8();
1461                                         }
1462                                         break;
1463                                 }
1464                                 default: {
1465                                         $length = $this->eat8();
1466                                         break;
1467                                 }
1468                         }
1469                         if ($length == 0) {
1470                                 return 0;
1471                         }
1472                         if ($length < 0) {
1473                                 return -1;
1474                         }
1475                         $this->dataStr = substr($this->dataStr, $length);
1476                 }
1477                 return 0;
1478         }
1479
1480         /**
1481          * toUpper
1482          */
1483          protected function toUpper() {
1484                 $stringLen = strlen($this->dataStr);
1485                 $p = 0;
1486                 while ($p < $stringLen) {
1487                         $mode = $this->identifyMode(substr($this->dataStr, $p), $this->hint);
1488                         if ($mode == QR_MODE_KJ) {
1489                                 $p += 2;
1490                         } else {
1491                                 if ((ord($this->dataStr[$p]) >= ord('a')) AND (ord($this->dataStr[$p]) <= ord('z'))) {
1492                                         $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
1493                                 }
1494                                 $p++;
1495                         }
1496                 }
1497                 return $this->dataStr;
1498         }
1499
1500         // - - - - - - - - - - - - - - - - - - - - - - - - -
1501
1502         // QRinputItem
1503
1504         /**
1505          * newInputItem
1506          * @param $mode (int)
1507          * @param $size (int)
1508          * @param $data (array)
1509          * @param $bstream (array)
1510          * @return array input item
1511          */
1512          protected function newInputItem($mode, $size, $data, $bstream=null) {
1513                 $setData = array_slice($data, 0, $size);
1514                 if (count($setData) < $size) {
1515                         $setData = array_merge($setData, array_fill(0, ($size - count($setData)), 0));
1516                 }
1517                 if (!$this->check($mode, $size, $setData)) {
1518                         return NULL;
1519                 }
1520                 $inputitem = array();
1521                 $inputitem['mode'] = $mode;
1522                 $inputitem['size'] = $size;
1523                 $inputitem['data'] = $setData;
1524                 $inputitem['bstream'] = $bstream;
1525                 return $inputitem;
1526         }
1527
1528         /**
1529          * encodeModeNum
1530          * @param $inputitem (array)
1531          * @param $version (int)
1532          * @return array input item
1533          */
1534          protected function encodeModeNum($inputitem, $version) {
1535                 $words = (int)($inputitem['size'] / 3);
1536                 $inputitem['bstream'] = array();
1537                 $val = 0x1;
1538                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1539                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_NM, $version), $inputitem['size']);
1540                 for ($i=0; $i < $words; ++$i) {
1541                         $val  = (ord($inputitem['data'][$i*3  ]) - ord('0')) * 100;
1542                         $val += (ord($inputitem['data'][$i*3+1]) - ord('0')) * 10;
1543                         $val += (ord($inputitem['data'][$i*3+2]) - ord('0'));
1544                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
1545                 }
1546                 if ($inputitem['size'] - $words * 3 == 1) {
1547                         $val = ord($inputitem['data'][$words*3]) - ord('0');
1548                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1549                 } elseif (($inputitem['size'] - ($words * 3)) == 2) {
1550                         $val  = (ord($inputitem['data'][$words*3  ]) - ord('0')) * 10;
1551                         $val += (ord($inputitem['data'][$words*3+1]) - ord('0'));
1552                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
1553                 }
1554                 return $inputitem;
1555         }
1556
1557         /**
1558          * encodeModeAn
1559          * @param $inputitem (array)
1560          * @param $version (int)
1561          * @return array input item
1562          */
1563          protected function encodeModeAn($inputitem, $version) {
1564                 $words = (int)($inputitem['size'] / 2);
1565                 $inputitem['bstream'] = array();
1566                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02);
1567                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_AN, $version), $inputitem['size']);
1568                 for ($i=0; $i < $words; ++$i) {
1569                         $val  = (int)($this->lookAnTable(ord($inputitem['data'][$i*2])) * 45);
1570                         $val += (int)($this->lookAnTable(ord($inputitem['data'][($i*2)+1])));
1571                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
1572                 }
1573                 if ($inputitem['size'] & 1) {
1574                         $val = $this->lookAnTable(ord($inputitem['data'][($words * 2)]));
1575                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
1576                 }
1577                 return $inputitem;
1578         }
1579
1580         /**
1581          * encodeMode8
1582          * @param $inputitem (array)
1583          * @param $version (int)
1584          * @return array input item
1585          */
1586          protected function encodeMode8($inputitem, $version) {
1587                 $inputitem['bstream'] = array();
1588                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
1589                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_8B, $version), $inputitem['size']);
1590                 for ($i=0; $i < $inputitem['size']; ++$i) {
1591                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][$i]));
1592                 }
1593                 return $inputitem;
1594         }
1595
1596         /**
1597          * encodeModeKanji
1598          * @param $inputitem (array)
1599          * @param $version (int)
1600          * @return array input item
1601          */
1602          protected function encodeModeKanji($inputitem, $version) {
1603                 $inputitem['bstream'] = array();
1604                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
1605                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_KJ, $version), (int)($inputitem['size'] / 2));
1606                 for ($i=0; $i<$inputitem['size']; $i+=2) {
1607                         $val = (ord($inputitem['data'][$i]) << 8) | ord($inputitem['data'][$i+1]);
1608                         if ($val <= 0x9ffc) {
1609                                 $val -= 0x8140;
1610                         } else {
1611                                 $val -= 0xc140;
1612                         }
1613                         $h = ($val >> 8) * 0xc0;
1614                         $val = ($val & 0xff) + $h;
1615                         $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
1616                 }
1617                 return $inputitem;
1618         }
1619
1620         /**
1621          * encodeModeStructure
1622          * @param $inputitem (array)
1623          * @return array input item
1624          */
1625          protected function encodeModeStructure($inputitem) {
1626                 $inputitem['bstream'] = array();
1627                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03);
1628                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][1]) - 1);
1629                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][0]) - 1);
1630                 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][2]));
1631                 return $inputitem;
1632         }
1633
1634         /**
1635          * encodeBitStream
1636          * @param $inputitem (array)
1637          * @param $version (int)
1638          * @return array input item
1639          */
1640          protected function encodeBitStream($inputitem, $version) {
1641                 $inputitem['bstream'] = array();
1642                 $words = $this->maximumWords($inputitem['mode'], $version);
1643                 if ($inputitem['size'] > $words) {
1644                         $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
1645                         $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, array_slice($inputitem['data'], $words));
1646                         $st1 = $this->encodeBitStream($st1, $version);
1647                         $st2 = $this->encodeBitStream($st2, $version);
1648                         $inputitem['bstream'] = array();
1649                         $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
1650                         $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
1651                 } else {
1652                         switch($inputitem['mode']) {
1653                                 case QR_MODE_NM: {
1654                                         $inputitem = $this->encodeModeNum($inputitem, $version);
1655                                         break;
1656                                 }
1657                                 case QR_MODE_AN: {
1658                                         $inputitem = $this->encodeModeAn($inputitem, $version);
1659                                         break;
1660                                 }
1661                                 case QR_MODE_8B: {
1662                                         $inputitem = $this->encodeMode8($inputitem, $version);
1663                                         break;
1664                                 }
1665                                 case QR_MODE_KJ: {
1666                                         $inputitem = $this->encodeModeKanji($inputitem, $version);
1667                                         break;
1668                                 }
1669                                 case QR_MODE_ST: {
1670                                         $inputitem = $this->encodeModeStructure($inputitem);
1671                                         break;
1672                                 }
1673                                 default: {
1674                                         break;
1675                                 }
1676                         }
1677                 }
1678                 return $inputitem;
1679         }
1680
1681         // - - - - - - - - - - - - - - - - - - - - - - - - -
1682
1683         // QRinput
1684
1685         /**
1686          * Append data to an input object.
1687          * The data is copied and appended to the input object.
1688          * @param $items (arrray) input items
1689          * @param $mode (int) encoding mode.
1690          * @param $size (int) size of data (byte).
1691          * @param $data (array) array of input data.
1692          * @return items
1693          *
1694          */
1695         protected function appendNewInputItem($items, $mode, $size, $data) {
1696                 $newitem = $this->newInputItem($mode, $size, $data);
1697                 if (!empty($newitem)) {
1698                         $items[] = $newitem;
1699                 }
1700                 return $items;
1701         }
1702
1703         /**
1704          * insertStructuredAppendHeader
1705          * @param $items (array)
1706          * @param $size (int)
1707          * @param $index (int)
1708          * @param $parity (int)
1709          * @return array items
1710          */
1711          protected function insertStructuredAppendHeader($items, $size, $index, $parity) {
1712                 if ($size > MAX_STRUCTURED_SYMBOLS) {
1713                         return -1;
1714                 }
1715                 if (($index <= 0) OR ($index > MAX_STRUCTURED_SYMBOLS)) {
1716                         return -1;
1717                 }
1718                 $buf = array($size, $index, $parity);
1719                 $entry = $this->newInputItem(QR_MODE_ST, 3, buf);
1720                 array_unshift($items, $entry);
1721                 return $items;
1722         }
1723
1724         /**
1725          * calcParity
1726          * @param $items (array)
1727          * @return int parity
1728          */
1729          protected function calcParity($items) {
1730                 $parity = 0;
1731                 foreach ($items as $item) {
1732                         if ($item['mode'] != QR_MODE_ST) {
1733                                 for ($i=$item['size']-1; $i>=0; --$i) {
1734                                         $parity ^= $item['data'][$i];
1735                                 }
1736                         }
1737                 }
1738                 return $parity;
1739         }
1740
1741         /**
1742          * checkModeNum
1743          * @param $size (int)
1744          * @param $data (array)
1745          * @return boolean true or false
1746          */
1747          protected function checkModeNum($size, $data) {
1748                 for ($i=0; $i<$size; ++$i) {
1749                         if ((ord($data[$i]) < ord('0')) OR (ord($data[$i]) > ord('9'))){
1750                                 return false;
1751                         }
1752                 }
1753                 return true;
1754         }
1755
1756         /**
1757          * Look up the alphabet-numeric convesion table (see JIS X0510:2004, pp.19).
1758          * @param $c (int) character value
1759          * @return value
1760          */
1761         protected function lookAnTable($c) {
1762                 return (($c > 127)?-1:$this->anTable[$c]);
1763         }
1764
1765         /**
1766          * checkModeAn
1767          * @param $size (int)
1768          * @param $data (array)
1769          * @return boolean true or false
1770          */
1771          protected function checkModeAn($size, $data) {
1772                 for ($i=0; $i<$size; ++$i) {
1773                         if ($this->lookAnTable(ord($data[$i])) == -1) {
1774                                 return false;
1775                         }
1776                 }
1777                 return true;
1778         }
1779
1780         /**
1781          * estimateBitsModeNum
1782          * @param $size (int)
1783          * @return int number of bits
1784          */
1785          protected function estimateBitsModeNum($size) {
1786                 $w = (int)($size / 3);
1787                 $bits = ($w * 10);
1788                 switch($size - ($w * 3)) {
1789                         case 1: {
1790                                 $bits += 4;
1791                                 break;
1792                         }
1793                         case 2: {
1794                                 $bits += 7;
1795                                 break;
1796                         }
1797                 }
1798                 return $bits;
1799         }
1800
1801         /**
1802          * estimateBitsModeAn
1803          * @param $size (int)
1804          * @return int number of bits
1805          */
1806          protected function estimateBitsModeAn($size) {
1807                 $bits = (int)($size * 5.5); // (size / 2 ) * 11
1808                 if ($size & 1) {
1809                         $bits += 6;
1810                 }
1811                 return $bits;
1812         }
1813
1814         /**
1815          * estimateBitsMode8
1816          * @param $size (int)
1817          * @return int number of bits
1818          */
1819          protected function estimateBitsMode8($size) {
1820                 return (int)($size * 8);
1821         }
1822
1823         /**
1824          * estimateBitsModeKanji
1825          * @param $size (int)
1826          * @return int number of bits
1827          */
1828          protected function estimateBitsModeKanji($size) {
1829                 return (int)($size * 6.5); // (size / 2 ) * 13
1830         }
1831
1832         /**
1833          * checkModeKanji
1834          * @param $size (int)
1835          * @param $data (array)
1836          * @return boolean true or false
1837          */
1838          protected function checkModeKanji($size, $data) {
1839                 if ($size & 1) {
1840                         return false;
1841                 }
1842                 for ($i=0; $i<$size; $i+=2) {
1843                         $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
1844                         if (($val < 0x8140) OR (($val > 0x9ffc) AND ($val < 0xe040)) OR ($val > 0xebbf)) {
1845                                 return false;
1846                         }
1847                 }
1848                 return true;
1849         }
1850
1851         /**
1852          * Validate the input data.
1853          * @param $mode (int) encoding mode.
1854          * @param $size (int) size of data (byte).
1855          * @param $data (array) data to validate
1856          * @return boolean true in case of valid data, false otherwise
1857          */
1858         protected function check($mode, $size, $data) {
1859                 if ($size <= 0) {
1860                         return false;
1861                 }
1862                 switch($mode) {
1863                         case QR_MODE_NM: {
1864                                 return $this->checkModeNum($size, $data);
1865                         }
1866                         case QR_MODE_AN: {
1867                                 return $this->checkModeAn($size, $data);
1868                         }
1869                         case QR_MODE_KJ: {
1870                                 return $this->checkModeKanji($size, $data);
1871                         }
1872                         case QR_MODE_8B: {
1873                                 return true;
1874                         }
1875                         case QR_MODE_ST: {
1876                                 return true;
1877                         }
1878                         default: {
1879                                 break;
1880                         }
1881                 }
1882                 return false;
1883         }
1884
1885         /**
1886          * estimateBitStreamSize
1887          * @param $items (array)
1888          * @param $version (int)
1889          * @return int bits
1890          */
1891          protected function estimateBitStreamSize($items, $version) {
1892                 $bits = 0;
1893                 if ($version == 0) {
1894                         $version = 1;
1895                 }
1896                 foreach ($items as $item) {
1897                         switch($item['mode']) {
1898                                 case QR_MODE_NM: {
1899                                         $bits = $this->estimateBitsModeNum($item['size']);
1900                                         break;
1901                                 }
1902                                 case QR_MODE_AN: {
1903                                         $bits = $this->estimateBitsModeAn($item['size']);
1904                                         break;
1905                                 }
1906                                 case QR_MODE_8B: {
1907                                         $bits = $this->estimateBitsMode8($item['size']);
1908                                         break;
1909                                 }
1910                                 case QR_MODE_KJ: {
1911                                         $bits = $this->estimateBitsModeKanji($item['size']);
1912                                         break;
1913                                 }
1914                                 case QR_MODE_ST: {
1915                                         return STRUCTURE_HEADER_BITS;
1916                                 }
1917                                 default: {
1918                                         return 0;
1919                                 }
1920                         }
1921                         $l = $this->lengthIndicator($item['mode'], $version);
1922                         $m = 1 << $l;
1923                         $num = (int)(($item['size'] + $m - 1) / $m);
1924                         $bits += $num * (4 + $l);
1925                 }
1926                 return $bits;
1927         }
1928
1929         /**
1930          * estimateVersion
1931          * @param $items (array)
1932          * @return int version
1933          */
1934          protected function estimateVersion($items) {
1935                 $version = 0;
1936                 $prev = 0;
1937                 do {
1938                         $prev = $version;
1939                         $bits = $this->estimateBitStreamSize($items, $prev);
1940                         $version = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
1941                         if ($version < 0) {
1942                                 return -1;
1943                         }
1944                 } while ($version > $prev);
1945                 return $version;
1946         }
1947
1948         /**
1949          * lengthOfCode
1950          * @param $mode (int)
1951          * @param $version (int)
1952          * @param $bits (int)
1953          * @return int size
1954          */
1955          protected function lengthOfCode($mode, $version, $bits) {
1956                 $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
1957                 switch($mode) {
1958                         case QR_MODE_NM: {
1959                                 $chunks = (int)($payload / 10);
1960                                 $remain = $payload - $chunks * 10;
1961                                 $size = $chunks * 3;
1962                                 if ($remain >= 7) {
1963                                         $size += 2;
1964                                 } elseif ($remain >= 4) {
1965                                         $size += 1;
1966                                 }
1967                                 break;
1968                         }
1969                         case QR_MODE_AN: {
1970                                 $chunks = (int)($payload / 11);
1971                                 $remain = $payload - $chunks * 11;
1972                                 $size = $chunks * 2;
1973                                 if ($remain >= 6) {
1974                                         ++$size;
1975                                 }
1976                                 break;
1977                         }
1978                         case QR_MODE_8B: {
1979                                 $size = (int)($payload / 8);
1980                                 break;
1981                         }
1982                         case QR_MODE_KJ: {
1983                                 $size = (int)(($payload / 13) * 2);
1984                                 break;
1985                         }
1986                         case QR_MODE_ST: {
1987                                 $size = (int)($payload / 8);
1988                                 break;
1989                         }
1990                         default: {
1991                                 $size = 0;
1992                                 break;
1993                         }
1994                 }
1995                 $maxsize = $this->maximumWords($mode, $version);
1996                 if ($size < 0) {
1997                         $size = 0;
1998                 }
1999                 if ($size > $maxsize) {
2000                         $size = $maxsize;
2001                 }
2002                 return $size;
2003         }
2004
2005         /**
2006          * createBitStream
2007          * @param $items (array)
2008          * @return array of items and total bits
2009          */
2010          protected function createBitStream($items) {
2011                 $total = 0;
2012                 foreach ($items as $key => $item) {
2013                         $items[$key] = $this->encodeBitStream($item, $this->version);
2014                         $bits = count($items[$key]['bstream']);
2015                         $total += $bits;
2016                 }
2017                 return array($items, $total);
2018         }
2019
2020         /**
2021          * convertData
2022          * @param $items (array)
2023          * @return array items
2024          */
2025          protected function convertData($items) {
2026                 $ver = $this->estimateVersion($items);
2027                 if ($ver > $this->version) {
2028                         $this->version = $ver;
2029                 }
2030                 while (true) {
2031                         $cbs = $this->createBitStream($items);
2032                         $items = $cbs[0];
2033                         $bits = $cbs[1];
2034                         if ($bits < 0) {
2035                                 return -1;
2036                         }
2037                         $ver = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
2038                         if ($ver < 0) {
2039                                 return -1;
2040                         } elseif ($ver > $this->version) {
2041                                 $this->version = $ver;
2042                         } else {
2043                                 break;
2044                         }
2045                 }
2046                 return $items;
2047         }
2048
2049         /**
2050          * Append Padding Bit to bitstream
2051          * @param $bstream (array)
2052          * @return array bitstream
2053          */
2054          protected function appendPaddingBit($bstream) {
2055                 if (is_null($bstream)) {
2056                         return null;
2057                 }
2058                 $bits = count($bstream);
2059                 $maxwords = $this->getDataLength($this->version, $this->level);
2060                 $maxbits = $maxwords * 8;
2061                 if ($maxbits == $bits) {
2062                         return $bstream;
2063                 }
2064                 if ($maxbits - $bits < 5) {
2065                         return $this->appendNum($bstream, $maxbits - $bits, 0);
2066                 }
2067                 $bits += 4;
2068                 $words = (int)(($bits + 7) / 8);
2069                 $padding = array();
2070                 $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
2071                 $padlen = $maxwords - $words;
2072                 if ($padlen > 0) {
2073                         $padbuf = array();
2074                         for ($i=0; $i<$padlen; ++$i) {
2075                                 $padbuf[$i] = ($i&1)?0x11:0xec;
2076                         }
2077                         $padding = $this->appendBytes($padding, $padlen, $padbuf);
2078                 }
2079                 return $this->appendBitstream($bstream, $padding);
2080         }
2081
2082         /**
2083          * mergeBitStream
2084          * @param $items (array) items
2085          * @return array bitstream
2086          */
2087          protected function mergeBitStream($items) {
2088                 $items = $this->convertData($items);
2089                 if (!is_array($items)) {
2090                         return null;
2091                 }
2092                 $bstream = array();
2093                 foreach ($items as $item) {
2094                         $bstream = $this->appendBitstream($bstream, $item['bstream']);
2095                 }
2096                 return $bstream;
2097         }
2098
2099         /**
2100          * Returns a stream of bits.
2101          * @param $items (int)
2102          * @return array padded merged byte stream
2103          */
2104         protected function getBitStream($items) {
2105                 $bstream = $this->mergeBitStream($items);
2106                 return $this->appendPaddingBit($bstream);
2107         }
2108
2109         /**
2110          * Pack all bit streams padding bits into a byte array.
2111          * @param $items (int)
2112          * @return array padded merged byte stream
2113          */
2114         protected function getByteStream($items) {
2115                 $bstream = $this->getBitStream($items);
2116                 return $this->bitstreamToByte($bstream);
2117         }
2118
2119         // - - - - - - - - - - - - - - - - - - - - - - - - -
2120
2121         // QRbitstream
2122
2123         /**
2124          * Return an array with zeros
2125          * @param $setLength (int) array size
2126          * @return array
2127          */
2128          protected function allocate($setLength) {
2129                 return array_fill(0, $setLength, 0);
2130         }
2131
2132         /**
2133          * Return new bitstream from number
2134          * @param $bits (int) number of bits
2135          * @param $num (int) number
2136          * @return array bitstream
2137          */
2138          protected function newFromNum($bits, $num) {
2139                 $bstream = $this->allocate($bits);
2140                 $mask = 1 << ($bits - 1);
2141                 for ($i=0; $i<$bits; ++$i) {
2142                         if ($num & $mask) {
2143                                 $bstream[$i] = 1;
2144                         } else {
2145                                 $bstream[$i] = 0;
2146                         }
2147                         $mask = $mask >> 1;
2148                 }
2149                 return $bstream;
2150         }
2151
2152         /**
2153          * Return new bitstream from bytes
2154          * @param $size (int) size
2155          * @param $data (array) bytes
2156          * @return array bitstream
2157          */
2158          protected function newFromBytes($size, $data) {
2159                 $bstream = $this->allocate($size * 8);
2160                 $p=0;
2161                 for ($i=0; $i<$size; ++$i) {
2162                         $mask = 0x80;
2163                         for ($j=0; $j<8; ++$j) {
2164                                 if ($data[$i] & $mask) {
2165                                         $bstream[$p] = 1;
2166                                 } else {
2167                                         $bstream[$p] = 0;
2168                                 }
2169                                 $p++;
2170                                 $mask = $mask >> 1;
2171                         }
2172                 }
2173                 return $bstream;
2174         }
2175
2176         /**
2177          * Append one bitstream to another
2178          * @param $bitstream (array) original bitstream
2179          * @param $append (array) bitstream to append
2180          * @return array bitstream
2181          */
2182          protected function appendBitstream($bitstream, $append) {
2183                 if ((!is_array($append)) OR (count($append) == 0)) {
2184                         return $bitstream;
2185                 }
2186                 if (count($bitstream) == 0) {
2187                         return $append;
2188                 }
2189                 return array_values(array_merge($bitstream, $append));
2190         }
2191
2192         /**
2193          * Append one bitstream created from number to another
2194          * @param $bitstream (array) original bitstream
2195          * @param $bits (int) number of bits
2196          * @param $num (int) number
2197          * @return array bitstream
2198          */
2199          protected function appendNum($bitstream, $bits, $num) {
2200                 if ($bits == 0) {
2201                         return 0;
2202                 }
2203                 $b = $this->newFromNum($bits, $num);
2204                 return $this->appendBitstream($bitstream, $b);
2205         }
2206
2207         /**
2208          * Append one bitstream created from bytes to another
2209          * @param $bitstream (array) original bitstream
2210          * @param $size (int) size
2211          * @param $data (array) bytes
2212          * @return array bitstream
2213          */
2214          protected function appendBytes($bitstream, $size, $data) {
2215                 if ($size == 0) {
2216                         return 0;
2217                 }
2218                 $b = $this->newFromBytes($size, $data);
2219                 return $this->appendBitstream($bitstream, $b);
2220         }
2221
2222         /**
2223          * Convert bitstream to bytes
2224          * @param $bstream (array) original bitstream
2225          * @return array of bytes
2226          */
2227          protected function bitstreamToByte($bstream) {
2228                 if (is_null($bstream)) {
2229                         return null;
2230                 }
2231                 $size = count($bstream);
2232                 if ($size == 0) {
2233                         return array();
2234                 }
2235                 $data = array_fill(0, (int)(($size + 7) / 8), 0);
2236                 $bytes = (int)($size / 8);
2237                 $p = 0;
2238                 for ($i=0; $i<$bytes; $i++) {
2239                         $v = 0;
2240                         for ($j=0; $j<8; $j++) {
2241                                 $v = $v << 1;
2242                                 $v |= $bstream[$p];
2243                                 $p++;
2244                         }
2245                         $data[$i] = $v;
2246                 }
2247                 if ($size & 7) {
2248                         $v = 0;
2249                         for ($j=0; $j<($size & 7); $j++) {
2250                                 $v = $v << 1;
2251                                 $v |= $bstream[$p];
2252                                 $p++;
2253                         }
2254                         $data[$bytes] = $v;
2255                 }
2256                 return $data;
2257         }
2258
2259         // - - - - - - - - - - - - - - - - - - - - - - - - -
2260
2261         // QRspec
2262
2263         /**
2264          * Replace a value on the array at the specified position
2265          * @param $srctab (array)
2266          * @param $x (int) X position
2267          * @param $y (int) Y position
2268          * @param $repl (string) value to replace
2269          * @param $replLen (int) length of the repl string
2270          * @return array srctab
2271          */
2272          protected function qrstrset($srctab, $x, $y, $repl, $replLen=false) {
2273                 $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
2274                 return $srctab;
2275         }
2276
2277         /**
2278          * Return maximum data code length (bytes) for the version.
2279          * @param $version (int) version
2280          * @param $level (int) error correction level
2281          * @return int maximum size (bytes)
2282          */
2283         protected function getDataLength($version, $level) {
2284                 return $this->capacity[$version][QRCAP_WORDS] - $this->capacity[$version][QRCAP_EC][$level];
2285         }
2286
2287         /**
2288          * Return maximum error correction code length (bytes) for the version.
2289          * @param $version (int) version
2290          * @param $level (int) error correction level
2291          * @return int ECC size (bytes)
2292          */
2293         protected function getECCLength($version, $level){
2294                 return $this->capacity[$version][QRCAP_EC][$level];
2295         }
2296
2297         /**
2298          * Return the width of the symbol for the version.
2299          * @param $version (int) version
2300          * @return int width
2301          */
2302         protected function getWidth($version) {
2303                 return $this->capacity[$version][QRCAP_WIDTH];
2304         }
2305
2306         /**
2307          * Return the numer of remainder bits.
2308          * @param $version (int) version
2309          * @return int number of remainder bits
2310          */
2311         protected function getRemainder($version) {
2312                 return $this->capacity[$version][QRCAP_REMINDER];
2313         }
2314
2315         /**
2316          * Return a version number that satisfies the input code length.
2317          * @param $size (int) input code length (bytes)
2318          * @param $level (int) error correction level
2319          * @return int version number
2320          */
2321         protected function getMinimumVersion($size, $level) {
2322                 for ($i = 1; $i <= QRSPEC_VERSION_MAX; ++$i) {
2323                         $words = ($this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]);
2324                         if ($words >= $size) {
2325                                 return $i;
2326                         }
2327                 }
2328                 // the size of input data is greater than QR capacity, try to lover the error correction mode
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 //============================================================+