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