PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.2.0
GiveWP – Donation Plugin and Fundraising Platform v2.2.0
4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / includes / libraries / tcpdf / include / barcodes / qrcode.php
give / includes / libraries / tcpdf / include / barcodes Last commit date
datamatrix.php 8 years ago pdf417.php 7 years ago qrcode.php 8 years ago
qrcode.php
2868 lines
1 <?php
2 //============================================================+
3 // File name : qrcode.php
4 // Version : 1.0.010
5 // Begin : 2010-03-22
6 // Last Update : 2012-07-25
7 // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8 // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9 // -------------------------------------------------------------------
10 // Copyright (C) 2010-2012 Nicola Asuni - Tecnick.com LTD
11 //
12 // This file is part of TCPDF software library.
13 //
14 // TCPDF is free software: you can redistribute it and/or modify it
15 // under the terms of the GNU Lesser General Public License as
16 // published by the Free Software Foundation, either version 3 of the
17 // License, or (at your option) any later version.
18 //
19 // TCPDF is distributed in the hope that it will be useful, but
20 // WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 // See the GNU Lesser General Public License for more details.
23 //
24 // You should have received a copy of the GNU Lesser General Public License
25 // along with TCPDF. If not, see <http://www.gnu.org/licenses/>.
26 //
27 // See LICENSE.TXT file for more information.
28 // -------------------------------------------------------------------
29 //
30 // DESCRIPTION :
31 //
32 // Class to create QR-code arrays for TCPDF class.
33 // QR Code symbol is a 2D barcode that can be scanned by
34 // handy terminals such as a mobile phone with CCD.
35 // The capacity of QR Code is up to 7000 digits or 4000
36 // characters, and has high robustness.
37 // This class supports QR Code model 2, described in
38 // JIS (Japanese Industrial Standards) X0510:2004
39 // or ISO/IEC 18004.
40 // Currently the following features are not supported:
41 // ECI and FNC1 mode, Micro QR Code, QR Code model 1,
42 // Structured mode.
43 //
44 // This class is derived from the following projects:
45 // ---------------------------------------------------------
46 // "PHP QR Code encoder"
47 // License: GNU-LGPLv3
48 // Copyright (C) 2010 by Dominik Dzienia <deltalab at poczta dot fm>
49 // http://phpqrcode.sourceforge.net/
50 // https://sourceforge.net/projects/phpqrcode/
51 //
52 // The "PHP QR Code encoder" is based on
53 // "C libqrencode library" (ver. 3.1.1)
54 // License: GNU-LGPL 2.1
55 // Copyright (C) 2006-2010 by Kentaro Fukuchi
56 // http://megaui.net/fukuchi/works/qrencode/index.en.html
57 //
58 // Reed-Solomon code encoder is written by Phil Karn, KA9Q.
59 // Copyright (C) 2002-2006 Phil Karn, KA9Q
60 //
61 // QR Code is registered trademark of DENSO WAVE INCORPORATED
62 // http://www.denso-wave.com/qrcode/index-e.html
63 // ---------------------------------------------------------
64 //============================================================+
65
66 /**
67 * @file
68 * Class to create QR-code arrays for TCPDF class.
69 * QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD.
70 * The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness.
71 * This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004.
72 * Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode.
73 *
74 * This class is derived from "PHP QR Code encoder" by Dominik Dzienia (http://phpqrcode.sourceforge.net/) based on "libqrencode C library 3.1.1." by Kentaro Fukuchi (http://megaui.net/fukuchi/works/qrencode/index.en.html), contains Reed-Solomon code written by Phil Karn, KA9Q. QR Code is registered trademark of DENSO WAVE INCORPORATED (http://www.denso-wave.com/qrcode/index-e.html).
75 * Please read comments on this class source file for full copyright and license information.
76 *
77 * @package com.tecnick.tcpdf
78 * @author Nicola Asuni
79 * @version 1.0.010
80 */
81
82 // definitions
83 if (!defined('QRCODEDEFS')) {
84
85 /**
86 * Indicate that definitions for this class are set
87 */
88 define('QRCODEDEFS', true);
89
90 // -----------------------------------------------------
91
92 // Encoding modes (characters which can be encoded in QRcode)
93
94 /**
95 * Encoding mode
96 */
97 define('QR_MODE_NL', -1);
98
99 /**
100 * Encoding mode numeric (0-9). 3 characters are encoded to 10bit length. In theory, 7089 characters or less can be stored in a QRcode.
101 */
102 define('QR_MODE_NM', 0);
103
104 /**
105 * Encoding mode alphanumeric (0-9A-Z $%*+-./:) 45characters. 2 characters are encoded to 11bit length. In theory, 4296 characters or less can be stored in a QRcode.
106 */
107 define('QR_MODE_AN', 1);
108
109 /**
110 * Encoding mode 8bit byte data. In theory, 2953 characters or less can be stored in a QRcode.
111 */
112 define('QR_MODE_8B', 2);
113
114 /**
115 * Encoding mode KANJI. A KANJI character (multibyte character) is encoded to 13bit length. In theory, 1817 characters or less can be stored in a QRcode.
116 */
117 define('QR_MODE_KJ', 3);
118
119 /**
120 * Encoding mode STRUCTURED (currently unsupported)
121 */
122 define('QR_MODE_ST', 4);
123
124 // -----------------------------------------------------
125
126 // Levels of error correction.
127 // QRcode has a function of an error correcting for miss reading that white is black.
128 // Error correcting is defined in 4 level as below.
129
130 /**
131 * Error correction level L : About 7% or less errors can be corrected.
132 */
133 define('QR_ECLEVEL_L', 0);
134
135 /**
136 * Error correction level M : About 15% or less errors can be corrected.
137 */
138 define('QR_ECLEVEL_M', 1);
139
140 /**
141 * Error correction level Q : About 25% or less errors can be corrected.
142 */
143 define('QR_ECLEVEL_Q', 2);
144
145 /**
146 * Error correction level H : About 30% or less errors can be corrected.
147 */
148 define('QR_ECLEVEL_H', 3);
149
150 // -----------------------------------------------------
151
152 // Version. Size of QRcode is defined as version.
153 // Version is from 1 to 40.
154 // Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases.
155 // So version 40 is 177*177 matrix.
156
157 /**
158 * Maximum QR Code version.
159 */
160 define('QRSPEC_VERSION_MAX', 40);
161
162 /**
163 * Maximum matrix size for maximum version (version 40 is 177*177 matrix).
164 */
165 define('QRSPEC_WIDTH_MAX', 177);
166
167 // -----------------------------------------------------
168
169 /**
170 * Matrix index to get width from $capacity array.
171 */
172 define('QRCAP_WIDTH', 0);
173
174 /**
175 * Matrix index to get number of words from $capacity array.
176 */
177 define('QRCAP_WORDS', 1);
178
179 /**
180 * Matrix index to get remainder from $capacity array.
181 */
182 define('QRCAP_REMINDER', 2);
183
184 /**
185 * Matrix index to get error correction level from $capacity array.
186 */
187 define('QRCAP_EC', 3);
188
189 // -----------------------------------------------------
190
191 // Structure (currently usupported)
192
193 /**
194 * Number of header bits for structured mode
195 */
196 define('STRUCTURE_HEADER_BITS', 20);
197
198 /**
199 * Max number of symbols for structured mode
200 */
201 define('MAX_STRUCTURED_SYMBOLS', 16);
202
203 // -----------------------------------------------------
204
205 // Masks
206
207 /**
208 * Down point base value for case 1 mask pattern (concatenation of same color in a line or a column)
209 */
210 define('N1', 3);
211
212 /**
213 * Down point base value for case 2 mask pattern (module block of same color)
214 */
215 define('N2', 3);
216
217 /**
218 * Down point base value for case 3 mask pattern (1:1:3:1:1(dark:bright:dark:bright:dark)pattern in a line or a column)
219 */
220 define('N3', 40);
221
222 /**
223 * Down point base value for case 4 mask pattern (ration of dark modules in whole)
224 */
225 define('N4', 10);
226
227 // -----------------------------------------------------
228
229 // Optimization settings
230
231 /**
232 * if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
233 */
234 define('QR_FIND_BEST_MASK', true);
235
236 /**
237 * if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
238 */
239 define('QR_FIND_FROM_RANDOM', 2);
240
241 /**
242 * when QR_FIND_BEST_MASK === false
243 */
244 define('QR_DEFAULT_MASK', 2);
245
246 // -----------------------------------------------------
247
248 } // end of definitions
249
250 // #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#
251
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
724 if (is_null($this->datacode)) {
725 return NULL;
726 }
727 $spec = $this->getEccSpec($this->version, $this->level, $spec);
728 $this->b1 = $this->rsBlockNum1($spec);
729 $this->dataLength = $this->rsDataLength($spec);
730 $this->eccLength = $this->rsEccLength($spec);
731 $this->ecccode = array_fill(0, $this->eccLength, 0);
732 $this->blocks = $this->rsBlockNum($spec);
733 $ret = $this->init($spec);
734 if ($ret < 0) {
735 return NULL;
736 }
737 $this->count = 0;
738 $this->width = $this->getWidth($this->version);
739 $this->frame = $this->newFrame($this->version);
740 $this->x = $this->width - 1;
741 $this->y = $this->width - 1;
742 $this->dir = -1;
743 $this->bit = -1;
744 // inteleaved data and ecc codes
745 for ($i=0; $i < ($this->dataLength + $this->eccLength); $i++) {
746 $code = $this->getCode();
747 $bit = 0x80;
748 for ($j=0; $j<8; $j++) {
749 $addr = $this->getNextPosition();
750 $this->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
751 $bit = $bit >> 1;
752 }
753 }
754 // remainder bits
755 $j = $this->getRemainder($this->version);
756 for ($i=0; $i<$j; $i++) {
757 $addr = $this->getNextPosition();
758 $this->setFrameAt($addr, 0x02);
759 }
760 // masking
761 $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
762 if ($mask < 0) {
763 if (QR_FIND_BEST_MASK) {
764 $masked = $this->mask($this->width, $this->frame, $this->level);
765 } else {
766 $masked = $this->makeMask($this->width, $this->frame, (intval(QR_DEFAULT_MASK) % 8), $this->level);
767 }
768 } else {
769 $masked = $this->makeMask($this->width, $this->frame, $mask, $this->level);
770 }
771 if ($masked == NULL) {
772 return NULL;
773 }
774 $this->data = $masked;
775 }
776
777 // - - - - - - - - - - - - - - - - - - - - - - - - -
778
779 // FrameFiller
780
781 /**
782 * Set frame value at specified position
783 * @param $at (array) x,y position
784 * @param $val (int) value of the character to set
785 */
786 protected function setFrameAt($at, $val) {
787 $this->frame[$at['y']][$at['x']] = chr($val);
788 }
789
790 /**
791 * Get frame value at specified position
792 * @param $at (array) x,y position
793 * @return value at specified position
794 */
795 protected function getFrameAt($at) {
796 return ord($this->frame[$at['y']][$at['x']]);
797 }
798
799 /**
800 * Return the next frame position
801 * @return array of x,y coordinates
802 */
803 protected function getNextPosition() {
804 do {
805 if ($this->bit == -1) {
806 $this->bit = 0;
807 return array('x'=>$this->x, 'y'=>$this->y);
808 }
809 $x = $this->x;
810 $y = $this->y;
811 $w = $this->width;
812 if ($this->bit == 0) {
813 $x--;
814 $this->bit++;
815 } else {
816 $x++;
817 $y += $this->dir;
818 $this->bit--;
819 }
820 if ($this->dir < 0) {
821 if ($y < 0) {
822 $y = 0;
823 $x -= 2;
824 $this->dir = 1;
825 if ($x == 6) {
826 $x--;
827 $y = 9;
828 }
829 }
830 } else {
831 if ($y == $w) {
832 $y = $w - 1;
833 $x -= 2;
834 $this->dir = -1;
835 if ($x == 6) {
836 $x--;
837 $y -= 8;
838 }
839 }
840 }
841 if (($x < 0) OR ($y < 0)) {
842 return NULL;
843 }
844 $this->x = $x;
845 $this->y = $y;
846 } while(ord($this->frame[$y][$x]) & 0x80);
847 return array('x'=>$x, 'y'=>$y);
848 }
849
850 // - - - - - - - - - - - - - - - - - - - - - - - - -
851
852 // QRrawcode
853
854 /**
855 * Initialize code.
856 * @param $spec (array) array of ECC specification
857 * @return 0 in case of success, -1 in case of error
858 */
859 protected function init($spec) {
860 $dl = $this->rsDataCodes1($spec);
861 $el = $this->rsEccCodes1($spec);
862 $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
863 $blockNo = 0;
864 $dataPos = 0;
865 $eccPos = 0;
866 $endfor = $this->rsBlockNum1($spec);
867 for ($i=0; $i < $endfor; ++$i) {
868 $ecc = array_slice($this->ecccode, $eccPos);
869 $this->rsblocks[$blockNo] = array();
870 $this->rsblocks[$blockNo]['dataLength'] = $dl;
871 $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos);
872 $this->rsblocks[$blockNo]['eccLength'] = $el;
873 $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
874 $this->rsblocks[$blockNo]['ecc'] = $ecc;
875 $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
876 $dataPos += $dl;
877 $eccPos += $el;
878 $blockNo++;
879 }
880 if ($this->rsBlockNum2($spec) == 0) {
881 return 0;
882 }
883 $dl = $this->rsDataCodes2($spec);
884 $el = $this->rsEccCodes2($spec);
885 $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
886 if ($rs == NULL) {
887 return -1;
888 }
889 $endfor = $this->rsBlockNum2($spec);
890 for ($i=0; $i < $endfor; ++$i) {
891 $ecc = array_slice($this->ecccode, $eccPos);
892 $this->rsblocks[$blockNo] = array();
893 $this->rsblocks[$blockNo]['dataLength'] = $dl;
894 $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos);
895 $this->rsblocks[$blockNo]['eccLength'] = $el;
896 $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
897 $this->rsblocks[$blockNo]['ecc'] = $ecc;
898 $this->ecccode = array_merge(array_slice($this->ecccode, 0, $eccPos), $ecc);
899 $dataPos += $dl;
900 $eccPos += $el;
901 $blockNo++;
902 }
903 return 0;
904 }
905
906 /**
907 * Return Reed-Solomon block code.
908 * @return array rsblocks
909 */
910 protected function getCode() {
911 if ($this->count < $this->dataLength) {
912 $row = $this->count % $this->blocks;
913 $col = $this->count / $this->blocks;
914 if ($col >= $this->rsblocks[0]['dataLength']) {
915 $row += $this->b1;
916 }
917 $ret = $this->rsblocks[$row]['data'][$col];
918 } elseif ($this->count < $this->dataLength + $this->eccLength) {
919 $row = ($this->count - $this->dataLength) % $this->blocks;
920 $col = ($this->count - $this->dataLength) / $this->blocks;
921 $ret = $this->rsblocks[$row]['ecc'][$col];
922 } else {
923 return 0;
924 }
925 $this->count++;
926 return $ret;
927 }
928
929 // - - - - - - - - - - - - - - - - - - - - - - - - -
930
931 // QRmask
932
933 /**
934 * Write Format Information on frame and returns the number of black bits
935 * @param $width (int) frame width
936 * @param $frame (array) frame
937 * @param $mask (array) masking mode
938 * @param $level (int) error correction level
939 * @return int blacks
940 */
941 protected function writeFormatInformation($width, &$frame, $mask, $level) {
942 $blacks = 0;
943 $format = $this->getFormatInfo($mask, $level);
944 for ($i=0; $i<8; ++$i) {
945 if ($format & 1) {
946 $blacks += 2;
947 $v = 0x85;
948 } else {
949 $v = 0x84;
950 }
951 $frame[8][$width - 1 - $i] = chr($v);
952 if ($i < 6) {
953 $frame[$i][8] = chr($v);
954 } else {
955 $frame[$i + 1][8] = chr($v);
956 }
957 $format = $format >> 1;
958 }
959 for ($i=0; $i<7; ++$i) {
960 if ($format & 1) {
961 $blacks += 2;
962 $v = 0x85;
963 } else {
964 $v = 0x84;
965 }
966 $frame[$width - 7 + $i][8] = chr($v);
967 if ($i == 0) {
968 $frame[8][7] = chr($v);
969 } else {
970 $frame[8][6 - $i] = chr($v);
971 }
972 $format = $format >> 1;
973 }
974 return $blacks;
975 }
976
977 /**
978 * mask0
979 * @param $x (int) X position
980 * @param $y (int) Y position
981 * @return int mask
982 */
983 protected function mask0($x, $y) {
984 return ($x + $y) & 1;
985 }
986
987 /**
988 * mask1
989 * @param $x (int) X position
990 * @param $y (int) Y position
991 * @return int mask
992 */
993 protected function mask1($x, $y) {
994 return ($y & 1);
995 }
996
997 /**
998 * mask2
999 * @param $x (int) X position
1000 * @param $y (int) Y position
1001 * @return int mask
1002 */
1003 protected function mask2($x, $y) {
1004 return ($x % 3);
1005 }
1006
1007 /**
1008 * mask3
1009 * @param $x (int) X position
1010 * @param $y (int) Y position
1011 * @return int mask
1012 */
1013 protected function mask3($x, $y) {
1014 return ($x + $y) % 3;
1015 }
1016
1017 /**
1018 * mask4
1019 * @param $x (int) X position
1020 * @param $y (int) Y position
1021 * @return int mask
1022 */
1023 protected function mask4($x, $y) {
1024 return (((int)($y / 2)) + ((int)($x / 3))) & 1;
1025 }
1026
1027 /**
1028 * mask5
1029 * @param $x (int) X position
1030 * @param $y (int) Y position
1031 * @return int mask
1032 */
1033 protected function mask5($x, $y) {
1034 return (($x * $y) & 1) + ($x * $y) % 3;
1035 }
1036
1037 /**
1038 * mask6
1039 * @param $x (int) X position
1040 * @param $y (int) Y position
1041 * @return int mask
1042 */
1043 protected function mask6($x, $y) {
1044 return ((($x * $y) & 1) + ($x * $y) % 3) & 1;
1045 }
1046
1047 /**
1048 * mask7
1049 * @param $x (int) X position
1050 * @param $y (int) Y position
1051 * @return int mask
1052 */
1053 protected function mask7($x, $y) {
1054 return ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
1055 }
1056
1057 /**
1058 * Return bitmask
1059 * @param $maskNo (int) mask number
1060 * @param $width (int) width
1061 * @param $frame (array) frame
1062 * @return array bitmask
1063 */
1064 protected function generateMaskNo($maskNo, $width, $frame) {
1065 $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
1066 for ($y=0; $y<$width; ++$y) {
1067 for ($x=0; $x<$width; ++$x) {
1068 if (ord($frame[$y][$x]) & 0x80) {
1069 $bitMask[$y][$x] = 0;
1070 } else {
1071 $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
1072 $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
1073 }
1074 }
1075 }
1076 return $bitMask;
1077 }
1078
1079 /**
1080 * makeMaskNo
1081 * @param $maskNo (int)
1082 * @param $width (int)
1083 * @param $s (int)
1084 * @param $d (int)
1085 * @param $maskGenOnly (boolean)
1086 * @return int b
1087 */
1088 protected function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly=false) {
1089 $b = 0;
1090 $bitMask = array();
1091 $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
1092 if ($maskGenOnly) {
1093 return;
1094 }
1095 $d = $s;
1096 for ($y=0; $y<$width; ++$y) {
1097 for ($x=0; $x<$width; ++$x) {
1098 if ($bitMask[$y][$x] == 1) {
1099 $d[$y][$x] = chr(ord($s[$y][$x]) ^ ((int)($bitMask[$y][$x])));
1100 }
1101 $b += (int)(ord($d[$y][$x]) & 1);
1102 }
1103 }
1104 return $b;
1105 }
1106
1107 /**
1108 * makeMask
1109 * @param $width (int)
1110 * @param $frame (array)
1111 * @param $maskNo (int)
1112 * @param $level (int)
1113 * @return array mask
1114 */
1115 protected function makeMask($width, $frame, $maskNo, $level) {
1116 $masked = array_fill(0, $width, str_repeat("\0", $width));
1117 $this->makeMaskNo($maskNo, $width, $frame, $masked);
1118 $this->writeFormatInformation($width, $masked, $maskNo, $level);
1119 return $masked;
1120 }
1121
1122 /**
1123 * calcN1N3
1124 * @param $length (int)
1125 * @return int demerit
1126 */
1127 protected function calcN1N3($length) {
1128 $demerit = 0;
1129 for ($i=0; $i<$length; ++$i) {
1130 if ($this->runLength[$i] >= 5) {
1131 $demerit += (N1 + ($this->runLength[$i] - 5));
1132 }
1133 if ($i & 1) {
1134 if (($i >= 3) AND ($i < ($length-2)) AND ($this->runLength[$i] % 3 == 0)) {
1135 $fact = (int)($this->runLength[$i] / 3);
1136 if (($this->runLength[$i-2] == $fact)
1137 AND ($this->runLength[$i-1] == $fact)
1138 AND ($this->runLength[$i+1] == $fact)
1139 AND ($this->runLength[$i+2] == $fact)) {
1140 if (($this->runLength[$i-3] < 0) OR ($this->runLength[$i-3] >= (4 * $fact))) {
1141 $demerit += N3;
1142 } elseif ((($i+3) >= $length) OR ($this->runLength[$i+3] >= (4 * $fact))) {
1143 $demerit += N3;
1144 }
1145 }
1146 }
1147 }
1148 }
1149 return $demerit;
1150 }
1151
1152 /**
1153 * evaluateSymbol
1154 * @param $width (int)
1155 * @param $frame (array)
1156 * @return int demerit
1157 */
1158 protected function evaluateSymbol($width, $frame) {
1159 $head = 0;
1160 $demerit = 0;
1161 for ($y=0; $y<$width; ++$y) {
1162 $head = 0;
1163 $this->runLength[0] = 1;
1164 $frameY = $frame[$y];
1165 if ($y > 0) {
1166 $frameYM = $frame[$y-1];
1167 }
1168 for ($x=0; $x<$width; ++$x) {
1169 if (($x > 0) AND ($y > 0)) {
1170 $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
1171 $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
1172 if (($b22 | ($w22 ^ 1)) & 1) {
1173 $demerit += N2;
1174 }
1175 }
1176 if (($x == 0) AND (ord($frameY[$x]) & 1)) {
1177 $this->runLength[0] = -1;
1178 $head = 1;
1179 $this->runLength[$head] = 1;
1180 } elseif ($x > 0) {
1181 if ((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
1182 $head++;
1183 $this->runLength[$head] = 1;
1184 } else {
1185 $this->runLength[$head]++;
1186 }
1187 }
1188 }
1189 $demerit += $this->calcN1N3($head+1);
1190 }
1191 for ($x=0; $x<$width; ++$x) {
1192 $head = 0;
1193 $this->runLength[0] = 1;
1194 for ($y=0; $y<$width; ++$y) {
1195 if (($y == 0) AND (ord($frame[$y][$x]) & 1)) {
1196 $this->runLength[0] = -1;
1197 $head = 1;
1198 $this->runLength[$head] = 1;
1199 } elseif ($y > 0) {
1200 if ((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
1201 $head++;
1202 $this->runLength[$head] = 1;
1203 } else {
1204 $this->runLength[$head]++;
1205 }
1206 }
1207 }
1208 $demerit += $this->calcN1N3($head+1);
1209 }
1210 return $demerit;
1211 }
1212
1213 /**
1214 * mask
1215 * @param $width (int)
1216 * @param $frame (array)
1217 * @param $level (int)
1218 * @return array best mask
1219 */
1220 protected function mask($width, $frame, $level) {
1221 $minDemerit = PHP_INT_MAX;
1222 $bestMaskNum = 0;
1223 $bestMask = array();
1224 $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
1225 if (QR_FIND_FROM_RANDOM !== false) {
1226 $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9);
1227 for ($i = 0; $i < $howManuOut; ++$i) {
1228 $remPos = rand (0, count($checked_masks)-1);
1229 unset($checked_masks[$remPos]);
1230 $checked_masks = array_values($checked_masks);
1231 }
1232 }
1233 $bestMask = $frame;
1234 foreach ($checked_masks as $i) {
1235 $mask = array_fill(0, $width, str_repeat("\0", $width));
1236 $demerit = 0;
1237 $blacks = 0;
1238 $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
1239 $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
1240 $blacks = (int)(100 * $blacks / ($width * $width));
1241 $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
1242 $demerit += $this->evaluateSymbol($width, $mask);
1243 if ($demerit < $minDemerit) {
1244 $minDemerit = $demerit;
1245 $bestMask = $mask;
1246 $bestMaskNum = $i;
1247 }
1248 }
1249 return $bestMask;
1250 }
1251
1252 // - - - - - - - - - - - - - - - - - - - - - - - - -
1253
1254 // QRsplit
1255
1256 /**
1257 * Return true if the character at specified position is a number
1258 * @param $str (string) string
1259 * @param $pos (int) characted position
1260 * @return boolean true of false
1261 */
1262 protected function isdigitat($str, $pos) {
1263 if ($pos >= strlen($str)) {
1264 return false;
1265 }
1266 return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
1267 }
1268
1269 /**
1270 * Return true if the character at specified position is an alphanumeric character
1271 * @param $str (string) string
1272 * @param $pos (int) characted position
1273 * @return boolean true of false
1274 */
1275 protected function isalnumat($str, $pos) {
1276 if ($pos >= strlen($str)) {
1277 return false;
1278 }
1279 return ($this->lookAnTable(ord($str[$pos])) >= 0);
1280 }
1281
1282 /**
1283 * identifyMode
1284 * @param $pos (int)
1285 * @return int mode
1286 */
1287 protected function identifyMode($pos) {
1288 if ($pos >= strlen($this->dataStr)) {
1289 return QR_MODE_NL;
1290 }
1291 $c = $this->dataStr[$pos];
1292 if ($this->isdigitat($this->dataStr, $pos)) {
1293 return QR_MODE_NM;
1294 } elseif ($this->isalnumat($this->dataStr, $pos)) {
1295 return QR_MODE_AN;
1296 } elseif ($this->hint == QR_MODE_KJ) {
1297 if ($pos+1 < strlen($this->dataStr)) {
1298 $d = $this->dataStr[$pos+1];
1299 $word = (ord($c) << 8) | ord($d);
1300 if (($word >= 0x8140 && $word <= 0x9ffc) OR ($word >= 0xe040 && $word <= 0xebbf)) {
1301 return QR_MODE_KJ;
1302 }
1303 }
1304 }
1305 return QR_MODE_8B;
1306 }
1307
1308 /**
1309 * eatNum
1310 * @return int run
1311 */
1312 protected function eatNum() {
1313 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1314 $p = 0;
1315 while($this->isdigitat($this->dataStr, $p)) {
1316 $p++;
1317 }
1318 $run = $p;
1319 $mode = $this->identifyMode($p);
1320 if ($mode == QR_MODE_8B) {
1321 $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1322 + $this->estimateBitsMode8(1) // + 4 + l8
1323 - $this->estimateBitsMode8($run + 1); // - 4 - l8
1324 if ($dif > 0) {
1325 return $this->eat8();
1326 }
1327 }
1328 if ($mode == QR_MODE_AN) {
1329 $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1330 + $this->estimateBitsModeAn(1) // + 4 + la
1331 - $this->estimateBitsModeAn($run + 1);// - 4 - la
1332 if ($dif > 0) {
1333 return $this->eatAn();
1334 }
1335 }
1336 $this->items = $this->appendNewInputItem($this->items, QR_MODE_NM, $run, str_split($this->dataStr));
1337 return $run;
1338 }
1339
1340 /**
1341 * eatAn
1342 * @return int run
1343 */
1344 protected function eatAn() {
1345 $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
1346 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1347 $p =1 ;
1348 while($this->isalnumat($this->dataStr, $p)) {
1349 if ($this->isdigitat($this->dataStr, $p)) {
1350 $q = $p;
1351 while($this->isdigitat($this->dataStr, $q)) {
1352 $q++;
1353 }
1354 $dif = $this->estimateBitsModeAn($p) // + 4 + la
1355 + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1356 - $this->estimateBitsModeAn($q); // - 4 - la
1357 if ($dif < 0) {
1358 break;
1359 } else {
1360 $p = $q;
1361 }
1362 } else {
1363 $p++;
1364 }
1365 }
1366 $run = $p;
1367 if (!$this->isalnumat($this->dataStr, $p)) {
1368 $dif = $this->estimateBitsModeAn($run) + 4 + $la
1369 + $this->estimateBitsMode8(1) // + 4 + l8
1370 - $this->estimateBitsMode8($run + 1); // - 4 - l8
1371 if ($dif > 0) {
1372 return $this->eat8();
1373 }
1374 }
1375 $this->items = $this->appendNewInputItem($this->items, QR_MODE_AN, $run, str_split($this->dataStr));
1376 return $run;
1377 }
1378
1379 /**
1380 * eatKanji
1381 * @return int run
1382 */
1383 protected function eatKanji() {
1384 $p = 0;
1385 while($this->identifyMode($p) == QR_MODE_KJ) {
1386 $p += 2;
1387 }
1388 $this->items = $this->appendNewInputItem($this->items, QR_MODE_KJ, $p, str_split($this->dataStr));
1389 return $run;
1390 }
1391
1392 /**
1393 * eat8
1394 * @return int run
1395 */
1396 protected function eat8() {
1397 $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
1398 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1399 $p = 1;
1400 $dataStrLen = strlen($this->dataStr);
1401 while($p < $dataStrLen) {
1402 $mode = $this->identifyMode($p);
1403 if ($mode == QR_MODE_KJ) {
1404 break;
1405 }
1406 if ($mode == QR_MODE_NM) {
1407 $q = $p;
1408 while($this->isdigitat($this->dataStr, $q)) {
1409 $q++;
1410 }
1411 $dif = $this->estimateBitsMode8($p) // + 4 + l8
1412 + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1413 - $this->estimateBitsMode8($q); // - 4 - l8
1414 if ($dif < 0) {
1415 break;
1416 } else {
1417 $p = $q;
1418 }
1419 } elseif ($mode == QR_MODE_AN) {
1420 $q = $p;
1421 while($this->isalnumat($this->dataStr, $q)) {
1422 $q++;
1423 }
1424 $dif = $this->estimateBitsMode8($p) // + 4 + l8
1425 + $this->estimateBitsModeAn($q - $p) + 4 + $la
1426 - $this->estimateBitsMode8($q); // - 4 - l8
1427 if ($dif < 0) {
1428 break;
1429 } else {
1430 $p = $q;
1431 }
1432 } else {
1433 $p++;
1434 }
1435 }
1436 $run = $p;
1437 $this->items = $this->appendNewInputItem($this->items, QR_MODE_8B, $run, str_split($this->dataStr));
1438 return $run;
1439 }
1440
1441 /**
1442 * splitString
1443 * @return (int)
1444 */
1445 protected function splitString() {
1446 while (strlen($this->dataStr) > 0) {
1447 $mode = $this->identifyMode(0);
1448 switch ($mode) {
1449 case QR_MODE_NM: {
1450 $length = $this->eatNum();
1451 break;
1452 }
1453 case QR_MODE_AN: {
1454 $length = $this->eatAn();
1455 break;
1456 }
1457 case QR_MODE_KJ: {
1458 if ($hint == QR_MODE_KJ) {
1459 $length = $this->eatKanji();
1460 } else {
1461 $length = $this->eat8();
1462 }
1463 break;
1464 }
1465 default: {
1466 $length = $this->eat8();
1467 break;
1468 }
1469 }
1470 if ($length == 0) {
1471 return 0;
1472 }
1473 if ($length < 0) {
1474 return -1;
1475 }
1476 $this->dataStr = substr($this->dataStr, $length);
1477 }
1478 return 0;
1479 }
1480
1481 /**
1482 * toUpper
1483 */
1484 protected function toUpper() {
1485 $stringLen = strlen($this->dataStr);
1486 $p = 0;
1487 while ($p < $stringLen) {
1488 $mode = $this->identifyMode(substr($this->dataStr, $p), $this->hint);
1489 if ($mode == QR_MODE_KJ) {
1490 $p += 2;
1491 } else {
1492 if ((ord($this->dataStr[$p]) >= ord('a')) AND (ord($this->dataStr[$p]) <= ord('z'))) {
1493 $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
1494 }
1495 $p++;
1496 }
1497 }
1498 return $this->dataStr;
1499 }
1500
1501 // - - - - - - - - - - - - - - - - - - - - - - - - -
1502
1503 // QRinputItem
1504
1505 /**
1506 * newInputItem
1507 * @param $mode (int)
1508 * @param $size (int)
1509 * @param $data (array)
1510 * @param $bstream (array)
1511 * @return array input item
1512 */
1513 protected function newInputItem($mode, $size, $data, $bstream=null) {
1514 $setData = array_slice($data, 0, $size);
1515 if (count($setData) < $size) {
1516 $setData = array_merge($setData, array_fill(0, ($size - count($setData)), 0));
1517 }
1518 if (!$this->check($mode, $size, $setData)) {
1519 return NULL;
1520 }
1521 $inputitem = array();
1522 $inputitem['mode'] = $mode;
1523 $inputitem['size'] = $size;
1524 $inputitem['data'] = $setData;
1525 $inputitem['bstream'] = $bstream;
1526 return $inputitem;
1527 }
1528
1529 /**
1530 * encodeModeNum
1531 * @param $inputitem (array)
1532 * @param $version (int)
1533 * @return array input item
1534 */
1535 protected function encodeModeNum($inputitem, $version) {
1536 $words = (int)($inputitem['size'] / 3);
1537 $inputitem['bstream'] = array();
1538 $val = 0x1;
1539 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1540 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_NM, $version), $inputitem['size']);
1541 for ($i=0; $i < $words; ++$i) {
1542 $val = (ord($inputitem['data'][$i*3 ]) - ord('0')) * 100;
1543 $val += (ord($inputitem['data'][$i*3+1]) - ord('0')) * 10;
1544 $val += (ord($inputitem['data'][$i*3+2]) - ord('0'));
1545 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
1546 }
1547 if ($inputitem['size'] - $words * 3 == 1) {
1548 $val = ord($inputitem['data'][$words*3]) - ord('0');
1549 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1550 } elseif (($inputitem['size'] - ($words * 3)) == 2) {
1551 $val = (ord($inputitem['data'][$words*3 ]) - ord('0')) * 10;
1552 $val += (ord($inputitem['data'][$words*3+1]) - ord('0'));
1553 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
1554 }
1555 return $inputitem;
1556 }
1557
1558 /**
1559 * encodeModeAn
1560 * @param $inputitem (array)
1561 * @param $version (int)
1562 * @return array input item
1563 */
1564 protected function encodeModeAn($inputitem, $version) {
1565 $words = (int)($inputitem['size'] / 2);
1566 $inputitem['bstream'] = array();
1567 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02);
1568 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_AN, $version), $inputitem['size']);
1569 for ($i=0; $i < $words; ++$i) {
1570 $val = (int)($this->lookAnTable(ord($inputitem['data'][$i*2])) * 45);
1571 $val += (int)($this->lookAnTable(ord($inputitem['data'][($i*2)+1])));
1572 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
1573 }
1574 if ($inputitem['size'] & 1) {
1575 $val = $this->lookAnTable(ord($inputitem['data'][($words * 2)]));
1576 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
1577 }
1578 return $inputitem;
1579 }
1580
1581 /**
1582 * encodeMode8
1583 * @param $inputitem (array)
1584 * @param $version (int)
1585 * @return array input item
1586 */
1587 protected function encodeMode8($inputitem, $version) {
1588 $inputitem['bstream'] = array();
1589 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
1590 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_8B, $version), $inputitem['size']);
1591 for ($i=0; $i < $inputitem['size']; ++$i) {
1592 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][$i]));
1593 }
1594 return $inputitem;
1595 }
1596
1597 /**
1598 * encodeModeKanji
1599 * @param $inputitem (array)
1600 * @param $version (int)
1601 * @return array input item
1602 */
1603 protected function encodeModeKanji($inputitem, $version) {
1604 $inputitem['bstream'] = array();
1605 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
1606 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_KJ, $version), (int)($inputitem['size'] / 2));
1607 for ($i=0; $i<$inputitem['size']; $i+=2) {
1608 $val = (ord($inputitem['data'][$i]) << 8) | ord($inputitem['data'][$i+1]);
1609 if ($val <= 0x9ffc) {
1610 $val -= 0x8140;
1611 } else {
1612 $val -= 0xc140;
1613 }
1614 $h = ($val >> 8) * 0xc0;
1615 $val = ($val & 0xff) + $h;
1616 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
1617 }
1618 return $inputitem;
1619 }
1620
1621 /**
1622 * encodeModeStructure
1623 * @param $inputitem (array)
1624 * @return array input item
1625 */
1626 protected function encodeModeStructure($inputitem) {
1627 $inputitem['bstream'] = array();
1628 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03);
1629 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][1]) - 1);
1630 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][0]) - 1);
1631 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][2]));
1632 return $inputitem;
1633 }
1634
1635 /**
1636 * encodeBitStream
1637 * @param $inputitem (array)
1638 * @param $version (int)
1639 * @return array input item
1640 */
1641 protected function encodeBitStream($inputitem, $version) {
1642 $inputitem['bstream'] = array();
1643 $words = $this->maximumWords($inputitem['mode'], $version);
1644 if ($inputitem['size'] > $words) {
1645 $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
1646 $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, array_slice($inputitem['data'], $words));
1647 $st1 = $this->encodeBitStream($st1, $version);
1648 $st2 = $this->encodeBitStream($st2, $version);
1649 $inputitem['bstream'] = array();
1650 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
1651 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
1652 } else {
1653 switch($inputitem['mode']) {
1654 case QR_MODE_NM: {
1655 $inputitem = $this->encodeModeNum($inputitem, $version);
1656 break;
1657 }
1658 case QR_MODE_AN: {
1659 $inputitem = $this->encodeModeAn($inputitem, $version);
1660 break;
1661 }
1662 case QR_MODE_8B: {
1663 $inputitem = $this->encodeMode8($inputitem, $version);
1664 break;
1665 }
1666 case QR_MODE_KJ: {
1667 $inputitem = $this->encodeModeKanji($inputitem, $version);
1668 break;
1669 }
1670 case QR_MODE_ST: {
1671 $inputitem = $this->encodeModeStructure($inputitem);
1672 break;
1673 }
1674 default: {
1675 break;
1676 }
1677 }
1678 }
1679 return $inputitem;
1680 }
1681
1682 // - - - - - - - - - - - - - - - - - - - - - - - - -
1683
1684 // QRinput
1685
1686 /**
1687 * Append data to an input object.
1688 * The data is copied and appended to the input object.
1689 * @param $items (arrray) input items
1690 * @param $mode (int) encoding mode.
1691 * @param $size (int) size of data (byte).
1692 * @param $data (array) array of input data.
1693 * @return items
1694 *
1695 */
1696 protected function appendNewInputItem($items, $mode, $size, $data) {
1697 $newitem = $this->newInputItem($mode, $size, $data);
1698 if (!empty($newitem)) {
1699 $items[] = $newitem;
1700 }
1701 return $items;
1702 }
1703
1704 /**
1705 * insertStructuredAppendHeader
1706 * @param $items (array)
1707 * @param $size (int)
1708 * @param $index (int)
1709 * @param $parity (int)
1710 * @return array items
1711 */
1712 protected function insertStructuredAppendHeader($items, $size, $index, $parity) {
1713 if ($size > MAX_STRUCTURED_SYMBOLS) {
1714 return -1;
1715 }
1716 if (($index <= 0) OR ($index > MAX_STRUCTURED_SYMBOLS)) {
1717 return -1;
1718 }
1719 $buf = array($size, $index, $parity);
1720 $entry = $this->newInputItem(QR_MODE_ST, 3, buf);
1721 array_unshift($items, $entry);
1722 return $items;
1723 }
1724
1725 /**
1726 * calcParity
1727 * @param $items (array)
1728 * @return int parity
1729 */
1730 protected function calcParity($items) {
1731 $parity = 0;
1732 foreach ($items as $item) {
1733 if ($item['mode'] != QR_MODE_ST) {
1734 for ($i=$item['size']-1; $i>=0; --$i) {
1735 $parity ^= $item['data'][$i];
1736 }
1737 }
1738 }
1739 return $parity;
1740 }
1741
1742 /**
1743 * checkModeNum
1744 * @param $size (int)
1745 * @param $data (array)
1746 * @return boolean true or false
1747 */
1748 protected function checkModeNum($size, $data) {
1749 for ($i=0; $i<$size; ++$i) {
1750 if ((ord($data[$i]) < ord('0')) OR (ord($data[$i]) > ord('9'))){
1751 return false;
1752 }
1753 }
1754 return true;
1755 }
1756
1757 /**
1758 * Look up the alphabet-numeric convesion table (see JIS X0510:2004, pp.19).
1759 * @param $c (int) character value
1760 * @return value
1761 */
1762 protected function lookAnTable($c) {
1763 return (($c > 127)?-1:$this->anTable[$c]);
1764 }
1765
1766 /**
1767 * checkModeAn
1768 * @param $size (int)
1769 * @param $data (array)
1770 * @return boolean true or false
1771 */
1772 protected function checkModeAn($size, $data) {
1773 for ($i=0; $i<$size; ++$i) {
1774 if ($this->lookAnTable(ord($data[$i])) == -1) {
1775 return false;
1776 }
1777 }
1778 return true;
1779 }
1780
1781 /**
1782 * estimateBitsModeNum
1783 * @param $size (int)
1784 * @return int number of bits
1785 */
1786 protected function estimateBitsModeNum($size) {
1787 $w = (int)($size / 3);
1788 $bits = ($w * 10);
1789 switch($size - ($w * 3)) {
1790 case 1: {
1791 $bits += 4;
1792 break;
1793 }
1794 case 2: {
1795 $bits += 7;
1796 break;
1797 }
1798 }
1799 return $bits;
1800 }
1801
1802 /**
1803 * estimateBitsModeAn
1804 * @param $size (int)
1805 * @return int number of bits
1806 */
1807 protected function estimateBitsModeAn($size) {
1808 $bits = (int)($size * 5.5); // (size / 2 ) * 11
1809 if ($size & 1) {
1810 $bits += 6;
1811 }
1812 return $bits;
1813 }
1814
1815 /**
1816 * estimateBitsMode8
1817 * @param $size (int)
1818 * @return int number of bits
1819 */
1820 protected function estimateBitsMode8($size) {
1821 return (int)($size * 8);
1822 }
1823
1824 /**
1825 * estimateBitsModeKanji
1826 * @param $size (int)
1827 * @return int number of bits
1828 */
1829 protected function estimateBitsModeKanji($size) {
1830 return (int)($size * 6.5); // (size / 2 ) * 13
1831 }
1832
1833 /**
1834 * checkModeKanji
1835 * @param $size (int)
1836 * @param $data (array)
1837 * @return boolean true or false
1838 */
1839 protected function checkModeKanji($size, $data) {
1840 if ($size & 1) {
1841 return false;
1842 }
1843 for ($i=0; $i<$size; $i+=2) {
1844 $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
1845 if (($val < 0x8140) OR (($val > 0x9ffc) AND ($val < 0xe040)) OR ($val > 0xebbf)) {
1846 return false;
1847 }
1848 }
1849 return true;
1850 }
1851
1852 /**
1853 * Validate the input data.
1854 * @param $mode (int) encoding mode.
1855 * @param $size (int) size of data (byte).
1856 * @param $data (array) data to validate
1857 * @return boolean true in case of valid data, false otherwise
1858 */
1859 protected function check($mode, $size, $data) {
1860 if ($size <= 0) {
1861 return false;
1862 }
1863 switch($mode) {
1864 case QR_MODE_NM: {
1865 return $this->checkModeNum($size, $data);
1866 }
1867 case QR_MODE_AN: {
1868 return $this->checkModeAn($size, $data);
1869 }
1870 case QR_MODE_KJ: {
1871 return $this->checkModeKanji($size, $data);
1872 }
1873 case QR_MODE_8B: {
1874 return true;
1875 }
1876 case QR_MODE_ST: {
1877 return true;
1878 }
1879 default: {
1880 break;
1881 }
1882 }
1883 return false;
1884 }
1885
1886 /**
1887 * estimateBitStreamSize
1888 * @param $items (array)
1889 * @param $version (int)
1890 * @return int bits
1891 */
1892 protected function estimateBitStreamSize($items, $version) {
1893 $bits = 0;
1894 if ($version == 0) {
1895 $version = 1;
1896 }
1897 foreach ($items as $item) {
1898 switch($item['mode']) {
1899 case QR_MODE_NM: {
1900 $bits = $this->estimateBitsModeNum($item['size']);
1901 break;
1902 }
1903 case QR_MODE_AN: {
1904 $bits = $this->estimateBitsModeAn($item['size']);
1905 break;
1906 }
1907 case QR_MODE_8B: {
1908 $bits = $this->estimateBitsMode8($item['size']);
1909 break;
1910 }
1911 case QR_MODE_KJ: {
1912 $bits = $this->estimateBitsModeKanji($item['size']);
1913 break;
1914 }
1915 case QR_MODE_ST: {
1916 return STRUCTURE_HEADER_BITS;
1917 }
1918 default: {
1919 return 0;
1920 }
1921 }
1922 $l = $this->lengthIndicator($item['mode'], $version);
1923 $m = 1 << $l;
1924 $num = (int)(($item['size'] + $m - 1) / $m);
1925 $bits += $num * (4 + $l);
1926 }
1927 return $bits;
1928 }
1929
1930 /**
1931 * estimateVersion
1932 * @param $items (array)
1933 * @return int version
1934 */
1935 protected function estimateVersion($items) {
1936 $version = 0;
1937 $prev = 0;
1938 do {
1939 $prev = $version;
1940 $bits = $this->estimateBitStreamSize($items, $prev);
1941 $version = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
1942 if ($version < 0) {
1943 return -1;
1944 }
1945 } while ($version > $prev);
1946 return $version;
1947 }
1948
1949 /**
1950 * lengthOfCode
1951 * @param $mode (int)
1952 * @param $version (int)
1953 * @param $bits (int)
1954 * @return int size
1955 */
1956 protected function lengthOfCode($mode, $version, $bits) {
1957 $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
1958 switch($mode) {
1959 case QR_MODE_NM: {
1960 $chunks = (int)($payload / 10);
1961 $remain = $payload - $chunks * 10;
1962 $size = $chunks * 3;
1963 if ($remain >= 7) {
1964 $size += 2;
1965 } elseif ($remain >= 4) {
1966 $size += 1;
1967 }
1968 break;
1969 }
1970 case QR_MODE_AN: {
1971 $chunks = (int)($payload / 11);
1972 $remain = $payload - $chunks * 11;
1973 $size = $chunks * 2;
1974 if ($remain >= 6) {
1975 ++$size;
1976 }
1977 break;
1978 }
1979 case QR_MODE_8B: {
1980 $size = (int)($payload / 8);
1981 break;
1982 }
1983 case QR_MODE_KJ: {
1984 $size = (int)(($payload / 13) * 2);
1985 break;
1986 }
1987 case QR_MODE_ST: {
1988 $size = (int)($payload / 8);
1989 break;
1990 }
1991 default: {
1992 $size = 0;
1993 break;
1994 }
1995 }
1996 $maxsize = $this->maximumWords($mode, $version);
1997 if ($size < 0) {
1998 $size = 0;
1999 }
2000 if ($size > $maxsize) {
2001 $size = $maxsize;
2002 }
2003 return $size;
2004 }
2005
2006 /**
2007 * createBitStream
2008 * @param $items (array)
2009 * @return array of items and total bits
2010 */
2011 protected function createBitStream($items) {
2012 $total = 0;
2013 foreach ($items as $key => $item) {
2014 $items[$key] = $this->encodeBitStream($item, $this->version);
2015 $bits = count($items[$key]['bstream']);
2016 $total += $bits;
2017 }
2018 return array($items, $total);
2019 }
2020
2021 /**
2022 * convertData
2023 * @param $items (array)
2024 * @return array items
2025 */
2026 protected function convertData($items) {
2027 $ver = $this->estimateVersion($items);
2028 if ($ver > $this->version) {
2029 $this->version = $ver;
2030 }
2031 while (true) {
2032 $cbs = $this->createBitStream($items);
2033 $items = $cbs[0];
2034 $bits = $cbs[1];
2035 if ($bits < 0) {
2036 return -1;
2037 }
2038 $ver = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
2039 if ($ver < 0) {
2040 return -1;
2041 } elseif ($ver > $this->version) {
2042 $this->version = $ver;
2043 } else {
2044 break;
2045 }
2046 }
2047 return $items;
2048 }
2049
2050 /**
2051 * Append Padding Bit to bitstream
2052 * @param $bstream (array)
2053 * @return array bitstream
2054 */
2055 protected function appendPaddingBit($bstream) {
2056 if (is_null($bstream)) {
2057 return null;
2058 }
2059 $bits = count($bstream);
2060 $maxwords = $this->getDataLength($this->version, $this->level);
2061 $maxbits = $maxwords * 8;
2062 if ($maxbits == $bits) {
2063 return $bstream;
2064 }
2065 if ($maxbits - $bits < 5) {
2066 return $this->appendNum($bstream, $maxbits - $bits, 0);
2067 }
2068 $bits += 4;
2069 $words = (int)(($bits + 7) / 8);
2070 $padding = array();
2071 $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
2072 $padlen = $maxwords - $words;
2073 if ($padlen > 0) {
2074 $padbuf = array();
2075 for ($i=0; $i<$padlen; ++$i) {
2076 $padbuf[$i] = ($i&1)?0x11:0xec;
2077 }
2078 $padding = $this->appendBytes($padding, $padlen, $padbuf);
2079 }
2080 return $this->appendBitstream($bstream, $padding);
2081 }
2082
2083 /**
2084 * mergeBitStream
2085 * @param $items (array) items
2086 * @return array bitstream
2087 */
2088 protected function mergeBitStream($items) {
2089 $items = $this->convertData($items);
2090 if (!is_array($items)) {
2091 return null;
2092 }
2093 $bstream = array();
2094 foreach ($items as $item) {
2095 $bstream = $this->appendBitstream($bstream, $item['bstream']);
2096 }
2097 return $bstream;
2098 }
2099
2100 /**
2101 * Returns a stream of bits.
2102 * @param $items (int)
2103 * @return array padded merged byte stream
2104 */
2105 protected function getBitStream($items) {
2106 $bstream = $this->mergeBitStream($items);
2107 return $this->appendPaddingBit($bstream);
2108 }
2109
2110 /**
2111 * Pack all bit streams padding bits into a byte array.
2112 * @param $items (int)
2113 * @return array padded merged byte stream
2114 */
2115 protected function getByteStream($items) {
2116 $bstream = $this->getBitStream($items);
2117 return $this->bitstreamToByte($bstream);
2118 }
2119
2120 // - - - - - - - - - - - - - - - - - - - - - - - - -
2121
2122 // QRbitstream
2123
2124 /**
2125 * Return an array with zeros
2126 * @param $setLength (int) array size
2127 * @return array
2128 */
2129 protected function allocate($setLength) {
2130 return array_fill(0, $setLength, 0);
2131 }
2132
2133 /**
2134 * Return new bitstream from number
2135 * @param $bits (int) number of bits
2136 * @param $num (int) number
2137 * @return array bitstream
2138 */
2139 protected function newFromNum($bits, $num) {
2140 $bstream = $this->allocate($bits);
2141 $mask = 1 << ($bits - 1);
2142 for ($i=0; $i<$bits; ++$i) {
2143 if ($num & $mask) {
2144 $bstream[$i] = 1;
2145 } else {
2146 $bstream[$i] = 0;
2147 }
2148 $mask = $mask >> 1;
2149 }
2150 return $bstream;
2151 }
2152
2153 /**
2154 * Return new bitstream from bytes
2155 * @param $size (int) size
2156 * @param $data (array) bytes
2157 * @return array bitstream
2158 */
2159 protected function newFromBytes($size, $data) {
2160 $bstream = $this->allocate($size * 8);
2161 $p=0;
2162 for ($i=0; $i<$size; ++$i) {
2163 $mask = 0x80;
2164 for ($j=0; $j<8; ++$j) {
2165 if ($data[$i] & $mask) {
2166 $bstream[$p] = 1;
2167 } else {
2168 $bstream[$p] = 0;
2169 }
2170 $p++;
2171 $mask = $mask >> 1;
2172 }
2173 }
2174 return $bstream;
2175 }
2176
2177 /**
2178 * Append one bitstream to another
2179 * @param $bitstream (array) original bitstream
2180 * @param $append (array) bitstream to append
2181 * @return array bitstream
2182 */
2183 protected function appendBitstream($bitstream, $append) {
2184 if ((!is_array($append)) OR (count($append) == 0)) {
2185 return $bitstream;
2186 }
2187 if (count($bitstream) == 0) {
2188 return $append;
2189 }
2190 return array_values(array_merge($bitstream, $append));
2191 }
2192
2193 /**
2194 * Append one bitstream created from number to another
2195 * @param $bitstream (array) original bitstream
2196 * @param $bits (int) number of bits
2197 * @param $num (int) number
2198 * @return array bitstream
2199 */
2200 protected function appendNum($bitstream, $bits, $num) {
2201 if ($bits == 0) {
2202 return 0;
2203 }
2204 $b = $this->newFromNum($bits, $num);
2205 return $this->appendBitstream($bitstream, $b);
2206 }
2207
2208 /**
2209 * Append one bitstream created from bytes to another
2210 * @param $bitstream (array) original bitstream
2211 * @param $size (int) size
2212 * @param $data (array) bytes
2213 * @return array bitstream
2214 */
2215 protected function appendBytes($bitstream, $size, $data) {
2216 if ($size == 0) {
2217 return 0;
2218 }
2219 $b = $this->newFromBytes($size, $data);
2220 return $this->appendBitstream($bitstream, $b);
2221 }
2222
2223 /**
2224 * Convert bitstream to bytes
2225 * @param $bstream (array) original bitstream
2226 * @return array of bytes
2227 */
2228 protected function bitstreamToByte($bstream) {
2229 if (is_null($bstream)) {
2230 return null;
2231 }
2232 $size = count($bstream);
2233 if ($size == 0) {
2234 return array();
2235 }
2236 $data = array_fill(0, (int)(($size + 7) / 8), 0);
2237 $bytes = (int)($size / 8);
2238 $p = 0;
2239 for ($i=0; $i<$bytes; $i++) {
2240 $v = 0;
2241 for ($j=0; $j<8; $j++) {
2242 $v = $v << 1;
2243 $v |= $bstream[$p];
2244 $p++;
2245 }
2246 $data[$i] = $v;
2247 }
2248 if ($size & 7) {
2249 $v = 0;
2250 for ($j=0; $j<($size & 7); $j++) {
2251 $v = $v << 1;
2252 $v |= $bstream[$p];
2253 $p++;
2254 }
2255 $data[$bytes] = $v;
2256 }
2257 return $data;
2258 }
2259
2260 // - - - - - - - - - - - - - - - - - - - - - - - - -
2261
2262 // QRspec
2263
2264 /**
2265 * Replace a value on the array at the specified position
2266 * @param $srctab (array)
2267 * @param $x (int) X position
2268 * @param $y (int) Y position
2269 * @param $repl (string) value to replace
2270 * @param $replLen (int) length of the repl string
2271 * @return array srctab
2272 */
2273 protected function qrstrset($srctab, $x, $y, $repl, $replLen=false) {
2274 $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
2275 return $srctab;
2276 }
2277
2278 /**
2279 * Return maximum data code length (bytes) for the version.
2280 * @param $version (int) version
2281 * @param $level (int) error correction level
2282 * @return int maximum size (bytes)
2283 */
2284 protected function getDataLength($version, $level) {
2285 return $this->capacity[$version][QRCAP_WORDS] - $this->capacity[$version][QRCAP_EC][$level];
2286 }
2287
2288 /**
2289 * Return maximum error correction code length (bytes) for the version.
2290 * @param $version (int) version
2291 * @param $level (int) error correction level
2292 * @return int ECC size (bytes)
2293 */
2294 protected function getECCLength($version, $level){
2295 return $this->capacity[$version][QRCAP_EC][$level];
2296 }
2297
2298 /**
2299 * Return the width of the symbol for the version.
2300 * @param $version (int) version
2301 * @return int width
2302 */
2303 protected function getWidth($version) {
2304 return $this->capacity[$version][QRCAP_WIDTH];
2305 }
2306
2307 /**
2308 * Return the numer of remainder bits.
2309 * @param $version (int) version
2310 * @return int number of remainder bits
2311 */
2312 protected function getRemainder($version) {
2313 return $this->capacity[$version][QRCAP_REMINDER];
2314 }
2315
2316 /**
2317 * Return a version number that satisfies the input code length.
2318 * @param $size (int) input code length (bytes)
2319 * @param $level (int) error correction level
2320 * @return int version number
2321 */
2322 protected function getMinimumVersion($size, $level) {
2323 for ($i = 1; $i <= QRSPEC_VERSION_MAX; ++$i) {
2324 $words = ($this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]);
2325 if ($words >= $size) {
2326 return $i;
2327 }
2328 }
2329 // the size of input data is greater than QR capacity, try to lover the error correction mode
2330 return -1;
2331 }
2332
2333 /**
2334 * Return the size of length indicator for the mode and version.
2335 * @param $mode (int) encoding mode
2336 * @param $version (int) version
2337 * @return int the size of the appropriate length indicator (bits).
2338 */
2339 protected function lengthIndicator($mode, $version) {
2340 if ($mode == QR_MODE_ST) {
2341 return 0;
2342 }
2343 if ($version <= 9) {
2344 $l = 0;
2345 } elseif ($version <= 26) {
2346 $l = 1;
2347 } else {
2348 $l = 2;
2349 }
2350 return $this->lengthTableBits[$mode][$l];
2351 }
2352
2353 /**
2354 * Return the maximum length for the mode and version.
2355 * @param $mode (int) encoding mode
2356 * @param $version (int) version
2357 * @return int the maximum length (bytes)
2358 */
2359 protected function maximumWords($mode, $version) {
2360 if ($mode == QR_MODE_ST) {
2361 return 3;
2362 }
2363 if ($version <= 9) {
2364 $l = 0;
2365 } else if ($version <= 26) {
2366 $l = 1;
2367 } else {
2368 $l = 2;
2369 }
2370 $bits = $this->lengthTableBits[$mode][$l];
2371 $words = (1 << $bits) - 1;
2372 if ($mode == QR_MODE_KJ) {
2373 $words *= 2; // the number of bytes is required
2374 }
2375 return $words;
2376 }
2377
2378 /**
2379 * Return an array of ECC specification.
2380 * @param $version (int) version
2381 * @param $level (int) error correction level
2382 * @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}
2383 * @return array spec
2384 */
2385 protected function getEccSpec($version, $level, $spec) {
2386 if (count($spec) < 5) {
2387 $spec = array(0, 0, 0, 0, 0);
2388 }
2389 $b1 = $this->eccTable[$version][$level][0];
2390 $b2 = $this->eccTable[$version][$level][1];
2391 $data = $this->getDataLength($version, $level);
2392 $ecc = $this->getECCLength($version, $level);
2393 if ($b2 == 0) {
2394 $spec[0] = $b1;
2395 $spec[1] = (int)($data / $b1);
2396 $spec[2] = (int)($ecc / $b1);
2397 $spec[3] = 0;
2398 $spec[4] = 0;
2399 } else {
2400 $spec[0] = $b1;
2401 $spec[1] = (int)($data / ($b1 + $b2));
2402 $spec[2] = (int)($ecc / ($b1 + $b2));
2403 $spec[3] = $b2;
2404 $spec[4] = $spec[1] + 1;
2405 }
2406 return $spec;
2407 }
2408
2409 /**
2410 * Put an alignment marker.
2411 * @param $frame (array) frame
2412 * @param $ox (int) X center coordinate of the pattern
2413 * @param $oy (int) Y center coordinate of the pattern
2414 * @return array frame
2415 */
2416 protected function putAlignmentMarker($frame, $ox, $oy) {
2417 $finder = array(
2418 "\xa1\xa1\xa1\xa1\xa1",
2419 "\xa1\xa0\xa0\xa0\xa1",
2420 "\xa1\xa0\xa1\xa0\xa1",
2421 "\xa1\xa0\xa0\xa0\xa1",
2422 "\xa1\xa1\xa1\xa1\xa1"
2423 );
2424 $yStart = $oy - 2;
2425 $xStart = $ox - 2;
2426 for ($y=0; $y < 5; $y++) {
2427 $frame = $this->qrstrset($frame, $xStart, $yStart+$y, $finder[$y]);
2428 }
2429 return $frame;
2430 }
2431
2432 /**
2433 * Put an alignment pattern.
2434 * @param $version (int) version
2435 * @param $frame (array) frame
2436 * @param $width (int) width
2437 * @return array frame
2438 */
2439 protected function putAlignmentPattern($version, $frame, $width) {
2440 if ($version < 2) {
2441 return $frame;
2442 }
2443 $d = $this->alignmentPattern[$version][1] - $this->alignmentPattern[$version][0];
2444 if ($d < 0) {
2445 $w = 2;
2446 } else {
2447 $w = (int)(($width - $this->alignmentPattern[$version][0]) / $d + 2);
2448 }
2449 if ($w * $w - 3 == 1) {
2450 $x = $this->alignmentPattern[$version][0];
2451 $y = $this->alignmentPattern[$version][0];
2452 $frame = $this->putAlignmentMarker($frame, $x, $y);
2453 return $frame;
2454 }
2455 $cx = $this->alignmentPattern[$version][0];
2456 $wo = $w - 1;
2457 for ($x=1; $x < $wo; ++$x) {
2458 $frame = $this->putAlignmentMarker($frame, 6, $cx);
2459 $frame = $this->putAlignmentMarker($frame, $cx, 6);
2460 $cx += $d;
2461 }
2462 $cy = $this->alignmentPattern[$version][0];
2463 for ($y=0; $y < $wo; ++$y) {
2464 $cx = $this->alignmentPattern[$version][0];
2465 for ($x=0; $x < $wo; ++$x) {
2466 $frame = $this->putAlignmentMarker($frame, $cx, $cy);
2467 $cx += $d;
2468 }
2469 $cy += $d;
2470 }
2471 return $frame;
2472 }
2473
2474 /**
2475 * Return BCH encoded version information pattern that is used for the symbol of version 7 or greater. Use lower 18 bits.
2476 * @param $version (int) version
2477 * @return BCH encoded version information pattern
2478 */
2479 protected function getVersionPattern($version) {
2480 if (($version < 7) OR ($version > QRSPEC_VERSION_MAX)) {
2481 return 0;
2482 }
2483 return $this->versionPattern[($version - 7)];
2484 }
2485
2486 /**
2487 * Return BCH encoded format information pattern.
2488 * @param $mask (array)
2489 * @param $level (int) error correction level
2490 * @return BCH encoded format information pattern
2491 */
2492 protected function getFormatInfo($mask, $level) {
2493 if (($mask < 0) OR ($mask > 7)) {
2494 return 0;
2495 }
2496 if (($level < 0) OR ($level > 3)) {
2497 return 0;
2498 }
2499 return $this->formatInfo[$level][$mask];
2500 }
2501
2502 /**
2503 * Put a finder pattern.
2504 * @param $frame (array) frame
2505 * @param $ox (int) X center coordinate of the pattern
2506 * @param $oy (int) Y center coordinate of the pattern
2507 * @return array frame
2508 */
2509 protected function putFinderPattern($frame, $ox, $oy) {
2510 $finder = array(
2511 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
2512 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
2513 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2514 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2515 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2516 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
2517 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
2518 );
2519 for ($y=0; $y < 7; $y++) {
2520 $frame = $this->qrstrset($frame, $ox, ($oy + $y), $finder[$y]);
2521 }
2522 return $frame;
2523 }
2524
2525 /**
2526 * Return a copy of initialized frame.
2527 * @param $version (int) version
2528 * @return Array of unsigned char.
2529 */
2530 protected function createFrame($version) {
2531 $width = $this->capacity[$version][QRCAP_WIDTH];
2532 $frameLine = str_repeat ("\0", $width);
2533 $frame = array_fill(0, $width, $frameLine);
2534 // Finder pattern
2535 $frame = $this->putFinderPattern($frame, 0, 0);
2536 $frame = $this->putFinderPattern($frame, $width - 7, 0);
2537 $frame = $this->putFinderPattern($frame, 0, $width - 7);
2538 // Separator
2539 $yOffset = $width - 7;
2540 for ($y=0; $y < 7; ++$y) {
2541 $frame[$y][7] = "\xc0";
2542 $frame[$y][$width - 8] = "\xc0";
2543 $frame[$yOffset][7] = "\xc0";
2544 ++$yOffset;
2545 }
2546 $setPattern = str_repeat("\xc0", 8);
2547 $frame = $this->qrstrset($frame, 0, 7, $setPattern);
2548 $frame = $this->qrstrset($frame, $width-8, 7, $setPattern);
2549 $frame = $this->qrstrset($frame, 0, $width - 8, $setPattern);
2550 // Format info
2551 $setPattern = str_repeat("\x84", 9);
2552 $frame = $this->qrstrset($frame, 0, 8, $setPattern);
2553 $frame = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8);
2554 $yOffset = $width - 8;
2555 for ($y=0; $y < 8; ++$y,++$yOffset) {
2556 $frame[$y][8] = "\x84";
2557 $frame[$yOffset][8] = "\x84";
2558 }
2559 // Timing pattern
2560 $wo = $width - 15;
2561 for ($i=1; $i < $wo; ++$i) {
2562 $frame[6][7+$i] = chr(0x90 | ($i & 1));
2563 $frame[7+$i][6] = chr(0x90 | ($i & 1));
2564 }
2565 // Alignment pattern
2566 $frame = $this->putAlignmentPattern($version, $frame, $width);
2567 // Version information
2568 if ($version >= 7) {
2569 $vinf = $this->getVersionPattern($version);
2570 $v = $vinf;
2571 for ($x=0; $x<6; ++$x) {
2572 for ($y=0; $y<3; ++$y) {
2573 $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
2574 $v = $v >> 1;
2575 }
2576 }
2577 $v = $vinf;
2578 for ($y=0; $y<6; ++$y) {
2579 for ($x=0; $x<3; ++$x) {
2580 $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
2581 $v = $v >> 1;
2582 }
2583 }
2584 }
2585 // and a little bit...
2586 $frame[$width - 8][8] = "\x81";
2587 return $frame;
2588 }
2589
2590 /**
2591 * Set new frame for the specified version.
2592 * @param $version (int) version
2593 * @return Array of unsigned char.
2594 */
2595 protected function newFrame($version) {
2596 if (($version < 1) OR ($version > QRSPEC_VERSION_MAX)) {
2597 return NULL;
2598 }
2599 if (!isset($this->frames[$version])) {
2600 $this->frames[$version] = $this->createFrame($version);
2601 }
2602 if (is_null($this->frames[$version])) {
2603 return NULL;
2604 }
2605 return $this->frames[$version];
2606 }
2607
2608 /**
2609 * Return block number 0
2610 * @param $spec (array)
2611 * @return int value
2612 */
2613 protected function rsBlockNum($spec) {
2614 return ($spec[0] + $spec[3]);
2615 }
2616
2617 /**
2618 * Return block number 1
2619 * @param $spec (array)
2620 * @return int value
2621 */
2622 protected function rsBlockNum1($spec) {
2623 return $spec[0];
2624 }
2625
2626 /**
2627 * Return data codes 1
2628 * @param $spec (array)
2629 * @return int value
2630 */
2631 protected function rsDataCodes1($spec) {
2632 return $spec[1];
2633 }
2634
2635 /**
2636 * Return ecc codes 1
2637 * @param $spec (array)
2638 * @return int value
2639 */
2640 protected function rsEccCodes1($spec) {
2641 return $spec[2];
2642 }
2643
2644 /**
2645 * Return block number 2
2646 * @param $spec (array)
2647 * @return int value
2648 */
2649 protected function rsBlockNum2($spec) {
2650 return $spec[3];
2651 }
2652
2653 /**
2654 * Return data codes 2
2655 * @param $spec (array)
2656 * @return int value
2657 */
2658 protected function rsDataCodes2($spec) {
2659 return $spec[4];
2660 }
2661
2662 /**
2663 * Return ecc codes 2
2664 * @param $spec (array)
2665 * @return int value
2666 */
2667 protected function rsEccCodes2($spec) {
2668 return $spec[2];
2669 }
2670
2671 /**
2672 * Return data length
2673 * @param $spec (array)
2674 * @return int value
2675 */
2676 protected function rsDataLength($spec) {
2677 return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]);
2678 }
2679
2680 /**
2681 * Return ecc length
2682 * @param $spec (array)
2683 * @return int value
2684 */
2685 protected function rsEccLength($spec) {
2686 return ($spec[0] + $spec[3]) * $spec[2];
2687 }
2688
2689 // - - - - - - - - - - - - - - - - - - - - - - - - -
2690
2691 // QRrs
2692
2693 /**
2694 * Initialize a Reed-Solomon codec and add it to existing rsitems
2695 * @param $symsize (int) symbol size, bits
2696 * @param $gfpoly (int) Field generator polynomial coefficients
2697 * @param $fcr (int) first root of RS code generator polynomial, index form
2698 * @param $prim (int) primitive element to generate polynomial roots
2699 * @param $nroots (int) RS code generator polynomial degree (number of roots)
2700 * @param $pad (int) padding bytes at front of shortened block
2701 * @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>.
2702 */
2703 protected function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
2704 foreach ($this->rsitems as $rs) {
2705 if (($rs['pad'] != $pad) OR ($rs['nroots'] != $nroots) OR ($rs['mm'] != $symsize)
2706 OR ($rs['gfpoly'] != $gfpoly) OR ($rs['fcr'] != $fcr) OR ($rs['prim'] != $prim)) {
2707 continue;
2708 }
2709 return $rs;
2710 }
2711 $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
2712 array_unshift($this->rsitems, $rs);
2713 return $rs;
2714 }
2715
2716 // - - - - - - - - - - - - - - - - - - - - - - - - -
2717
2718 // QRrsItem
2719
2720 /**
2721 * modnn
2722 * @param $rs (array) RS values
2723 * @param $x (int) X position
2724 * @return int X osition
2725 */
2726 protected function modnn($rs, $x) {
2727 while ($x >= $rs['nn']) {
2728 $x -= $rs['nn'];
2729 $x = ($x >> $rs['mm']) + ($x & $rs['nn']);
2730 }
2731 return $x;
2732 }
2733
2734 /**
2735 * Initialize a Reed-Solomon codec and returns an array of values.
2736 * @param $symsize (int) symbol size, bits
2737 * @param $gfpoly (int) Field generator polynomial coefficients
2738 * @param $fcr (int) first root of RS code generator polynomial, index form
2739 * @param $prim (int) primitive element to generate polynomial roots
2740 * @param $nroots (int) RS code generator polynomial degree (number of roots)
2741 * @param $pad (int) padding bytes at front of shortened block
2742 * @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>.
2743 */
2744 protected function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
2745 // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2)
2746 $rs = null;
2747 // Check parameter ranges
2748 if (($symsize < 0) OR ($symsize > 8)) {
2749 return $rs;
2750 }
2751 if (($fcr < 0) OR ($fcr >= (1<<$symsize))) {
2752 return $rs;
2753 }
2754 if (($prim <= 0) OR ($prim >= (1<<$symsize))) {
2755 return $rs;
2756 }
2757 if (($nroots < 0) OR ($nroots >= (1<<$symsize))) {
2758 return $rs;
2759 }
2760 if (($pad < 0) OR ($pad >= ((1<<$symsize) -1 - $nroots))) {
2761 return $rs;
2762 }
2763 $rs = array();
2764 $rs['mm'] = $symsize;
2765 $rs['nn'] = (1 << $symsize) - 1;
2766 $rs['pad'] = $pad;
2767 $rs['alpha_to'] = array_fill(0, ($rs['nn'] + 1), 0);
2768 $rs['index_of'] = array_fill(0, ($rs['nn'] + 1), 0);
2769 // PHP style macro replacement ;)
2770 $NN =& $rs['nn'];
2771 $A0 =& $NN;
2772 // Generate Galois field lookup tables
2773 $rs['index_of'][0] = $A0; // log(zero) = -inf
2774 $rs['alpha_to'][$A0] = 0; // alpha**-inf = 0
2775 $sr = 1;
2776 for ($i=0; $i<$rs['nn']; ++$i) {
2777 $rs['index_of'][$sr] = $i;
2778 $rs['alpha_to'][$i] = $sr;
2779 $sr <<= 1;
2780 if ($sr & (1 << $symsize)) {
2781 $sr ^= $gfpoly;
2782 }
2783 $sr &= $rs['nn'];
2784 }
2785 if ($sr != 1) {
2786 // field generator polynomial is not primitive!
2787 return NULL;
2788 }
2789 // Form RS code generator polynomial from its roots
2790 $rs['genpoly'] = array_fill(0, ($nroots + 1), 0);
2791 $rs['fcr'] = $fcr;
2792 $rs['prim'] = $prim;
2793 $rs['nroots'] = $nroots;
2794 $rs['gfpoly'] = $gfpoly;
2795 // Find prim-th root of 1, used in decoding
2796 for ($iprim=1; ($iprim % $prim) != 0; $iprim += $rs['nn']) {
2797 ; // intentional empty-body loop!
2798 }
2799 $rs['iprim'] = (int)($iprim / $prim);
2800 $rs['genpoly'][0] = 1;
2801 for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
2802 $rs['genpoly'][$i+1] = 1;
2803 // Multiply rs->genpoly[] by @**(root + x)
2804 for ($j = $i; $j > 0; --$j) {
2805 if ($rs['genpoly'][$j] != 0) {
2806 $rs['genpoly'][$j] = $rs['genpoly'][$j-1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)];
2807 } else {
2808 $rs['genpoly'][$j] = $rs['genpoly'][$j-1];
2809 }
2810 }
2811 // rs->genpoly[0] can never be zero
2812 $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)];
2813 }
2814 // convert rs->genpoly[] to index form for quicker encoding
2815 for ($i = 0; $i <= $nroots; ++$i) {
2816 $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]];
2817 }
2818 return $rs;
2819 }
2820
2821 /**
2822 * Encode a Reed-Solomon codec and returns the parity array
2823 * @param $rs (array) RS values
2824 * @param $data (array) data
2825 * @param $parity (array) parity
2826 * @return parity array
2827 */
2828 protected function encode_rs_char($rs, $data, $parity) {
2829 $MM =& $rs['mm']; // bits per symbol
2830 $NN =& $rs['nn']; // the total number of symbols in a RS block
2831 $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
2832 $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
2833 $GENPOLY =& $rs['genpoly']; // an array of NROOTS+1 elements containing the generator polynomial in index form
2834 $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
2835 $FCR =& $rs['fcr']; // first consecutive root, index form
2836 $PRIM =& $rs['prim']; // primitive element, index form
2837 $IPRIM =& $rs['iprim']; // prim-th root of 1, index form
2838 $PAD =& $rs['pad']; // the number of pad symbols in a block
2839 $A0 =& $NN;
2840 $parity = array_fill(0, $NROOTS, 0);
2841 for ($i=0; $i < ($NN - $NROOTS - $PAD); $i++) {
2842 $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
2843 if ($feedback != $A0) {
2844 // feedback term is non-zero
2845 // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
2846 // always be for the polynomials constructed by init_rs()
2847 $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback);
2848 for ($j=1; $j < $NROOTS; ++$j) {
2849 $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[($NROOTS - $j)])];
2850 }
2851 }
2852 // Shift
2853 array_shift($parity);
2854 if ($feedback != $A0) {
2855 array_push($parity, $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]);
2856 } else {
2857 array_push($parity, 0);
2858 }
2859 }
2860 return $parity;
2861 }
2862
2863 } // end QRcode class
2864
2865 //============================================================+
2866 // END OF FILE
2867 //============================================================+
2868