PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.16.3
GiveWP – Donation Plugin and Fundraising Platform v4.16.3
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 1 month ago pdf417.php 1 month ago qrcode.php 1 month ago
qrcode.php
2844 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 (https://www.gnu.org/copyleft/lesser.html)
9 // -------------------------------------------------------------------
10 // Copyright (C) 2010-2026 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 <https://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 = (int)($this->count / $this->blocks);
888 if ($col >= $this->rsblocks[0]['dataLength']) {
889 $row += $this->b1;
890 }
891 $row = (int) $row;
892 $ret = $this->rsblocks[$row]['data'][$col];
893 } elseif ($this->count < $this->dataLength + $this->eccLength) {
894 $row = ($this->count - $this->dataLength) % $this->blocks;
895 $col = (int)(($this->count - $this->dataLength) / $this->blocks);
896 $ret = $this->rsblocks[$row]['ecc'][$col];
897 } else {
898 return 0;
899 }
900 $this->count++;
901 return $ret;
902 }
903
904 // - - - - - - - - - - - - - - - - - - - - - - - - -
905
906 // QRmask
907
908 /**
909 * Write Format Information on frame and returns the number of black bits
910 * @param int $width frame width
911 * @param array $frame frame
912 * @param array $mask masking mode
913 * @param int $level error correction level
914 * @return int blacks
915 */
916 protected function writeFormatInformation($width, &$frame, $mask, $level) {
917 $blacks = 0;
918 $format = $this->getFormatInfo($mask, $level);
919 for ($i=0; $i<8; ++$i) {
920 if ($format & 1) {
921 $blacks += 2;
922 $v = 0x85;
923 } else {
924 $v = 0x84;
925 }
926 $frame[8][$width - 1 - $i] = chr($v);
927 if ($i < 6) {
928 $frame[$i][8] = chr($v);
929 } else {
930 $frame[$i + 1][8] = chr($v);
931 }
932 $format = $format >> 1;
933 }
934 for ($i=0; $i<7; ++$i) {
935 if ($format & 1) {
936 $blacks += 2;
937 $v = 0x85;
938 } else {
939 $v = 0x84;
940 }
941 $frame[$width - 7 + $i][8] = chr($v);
942 if ($i == 0) {
943 $frame[8][7] = chr($v);
944 } else {
945 $frame[8][6 - $i] = chr($v);
946 }
947 $format = $format >> 1;
948 }
949 return $blacks;
950 }
951
952 /**
953 * mask0
954 * @param int $x X position
955 * @param int $y Y position
956 * @return int mask
957 */
958 protected function mask0($x, $y) {
959 return ($x + $y) & 1;
960 }
961
962 /**
963 * mask1
964 * @param int $x X position
965 * @param int $y Y position
966 * @return int mask
967 */
968 protected function mask1($x, $y) {
969 return ($y & 1);
970 }
971
972 /**
973 * mask2
974 * @param int $x X position
975 * @param int $y Y position
976 * @return int mask
977 */
978 protected function mask2($x, $y) {
979 return ($x % 3);
980 }
981
982 /**
983 * mask3
984 * @param int $x X position
985 * @param int $y Y position
986 * @return int mask
987 */
988 protected function mask3($x, $y) {
989 return ($x + $y) % 3;
990 }
991
992 /**
993 * mask4
994 * @param int $x X position
995 * @param int $y Y position
996 * @return int mask
997 */
998 protected function mask4($x, $y) {
999 return (((int)($y / 2)) + ((int)($x / 3))) & 1;
1000 }
1001
1002 /**
1003 * mask5
1004 * @param int $x X position
1005 * @param int $y Y position
1006 * @return int mask
1007 */
1008 protected function mask5($x, $y) {
1009 return (($x * $y) & 1) + ($x * $y) % 3;
1010 }
1011
1012 /**
1013 * mask6
1014 * @param int $x X position
1015 * @param int $y Y position
1016 * @return int mask
1017 */
1018 protected function mask6($x, $y) {
1019 return ((($x * $y) & 1) + ($x * $y) % 3) & 1;
1020 }
1021
1022 /**
1023 * mask7
1024 * @param int $x X position
1025 * @param int $y Y position
1026 * @return int mask
1027 */
1028 protected function mask7($x, $y) {
1029 return ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
1030 }
1031
1032 /**
1033 * Return bitmask
1034 * @param int $maskNo mask number
1035 * @param int $width width
1036 * @param array $frame frame
1037 * @return array bitmask
1038 */
1039 protected function generateMaskNo($maskNo, $width, $frame) {
1040 $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
1041 for ($y=0; $y<$width; ++$y) {
1042 for ($x=0; $x<$width; ++$x) {
1043 if (ord($frame[$y][$x]) & 0x80) {
1044 $bitMask[$y][$x] = 0;
1045 } else {
1046 $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
1047 $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
1048 }
1049 }
1050 }
1051 return $bitMask;
1052 }
1053
1054 /**
1055 * makeMaskNo
1056 * @param int $maskNo
1057 * @param int $width
1058 * @param int $s
1059 * @param int $d
1060 * @param boolean $maskGenOnly
1061 * @return int b
1062 */
1063 protected function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly=false) {
1064 $b = 0;
1065 $bitMask = array();
1066 $bitMask = $this->generateMaskNo($maskNo, $width, $s);
1067 if ($maskGenOnly) {
1068 return;
1069 }
1070 $d = $s;
1071 for ($y=0; $y<$width; ++$y) {
1072 for ($x=0; $x<$width; ++$x) {
1073 if ($bitMask[$y][$x] == 1) {
1074 $d[$y][$x] = chr(ord($s[$y][$x]) ^ ((int)($bitMask[$y][$x])));
1075 }
1076 $b += (int)(ord($d[$y][$x]) & 1);
1077 }
1078 }
1079 return $b;
1080 }
1081
1082 /**
1083 * makeMask
1084 * @param int $width
1085 * @param array $frame
1086 * @param int $maskNo
1087 * @param int $level
1088 * @return array mask
1089 */
1090 protected function makeMask($width, $frame, $maskNo, $level) {
1091 $masked = array_fill(0, $width, str_repeat("\0", $width));
1092 $this->makeMaskNo($maskNo, $width, $frame, $masked);
1093 $this->writeFormatInformation($width, $masked, $maskNo, $level);
1094 return $masked;
1095 }
1096
1097 /**
1098 * calcN1N3
1099 * @param int $length
1100 * @return int demerit
1101 */
1102 protected function calcN1N3($length) {
1103 $demerit = 0;
1104 for ($i=0; $i<$length; ++$i) {
1105 if ($this->runLength[$i] >= 5) {
1106 $demerit += (N1 + ($this->runLength[$i] - 5));
1107 }
1108 if ($i & 1) {
1109 if (($i >= 3) AND ($i < ($length-2)) AND ($this->runLength[$i] % 3 == 0)) {
1110 $fact = (int)($this->runLength[$i] / 3);
1111 if (($this->runLength[$i-2] == $fact)
1112 AND ($this->runLength[$i-1] == $fact)
1113 AND ($this->runLength[$i+1] == $fact)
1114 AND ($this->runLength[$i+2] == $fact)) {
1115 if (($this->runLength[$i-3] < 0) OR ($this->runLength[$i-3] >= (4 * $fact))) {
1116 $demerit += N3;
1117 } elseif ((($i+3) >= $length) OR ($this->runLength[$i+3] >= (4 * $fact))) {
1118 $demerit += N3;
1119 }
1120 }
1121 }
1122 }
1123 }
1124 return $demerit;
1125 }
1126
1127 /**
1128 * evaluateSymbol
1129 * @param int $width
1130 * @param array $frame
1131 * @return int demerit
1132 */
1133 protected function evaluateSymbol($width, $frame) {
1134 $head = 0;
1135 $demerit = 0;
1136 for ($y=0; $y<$width; ++$y) {
1137 $head = 0;
1138 $this->runLength[0] = 1;
1139 $frameY = $frame[$y];
1140 if ($y > 0) {
1141 $frameYM = $frame[$y-1];
1142 }
1143 for ($x=0; $x<$width; ++$x) {
1144 if (($x > 0) AND ($y > 0)) {
1145 $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
1146 $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
1147 if (($b22 | ($w22 ^ 1)) & 1) {
1148 $demerit += N2;
1149 }
1150 }
1151 if (($x == 0) AND (ord($frameY[$x]) & 1)) {
1152 $this->runLength[0] = -1;
1153 $head = 1;
1154 $this->runLength[$head] = 1;
1155 } elseif ($x > 0) {
1156 if ((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
1157 $head++;
1158 $this->runLength[$head] = 1;
1159 } else {
1160 $this->runLength[$head]++;
1161 }
1162 }
1163 }
1164 $demerit += $this->calcN1N3($head+1);
1165 }
1166 for ($x=0; $x<$width; ++$x) {
1167 $head = 0;
1168 $this->runLength[0] = 1;
1169 for ($y=0; $y<$width; ++$y) {
1170 if (($y == 0) AND (ord($frame[$y][$x]) & 1)) {
1171 $this->runLength[0] = -1;
1172 $head = 1;
1173 $this->runLength[$head] = 1;
1174 } elseif ($y > 0) {
1175 if ((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
1176 $head++;
1177 $this->runLength[$head] = 1;
1178 } else {
1179 $this->runLength[$head]++;
1180 }
1181 }
1182 }
1183 $demerit += $this->calcN1N3($head+1);
1184 }
1185 return $demerit;
1186 }
1187
1188 /**
1189 * mask
1190 * @param int $width
1191 * @param array $frame
1192 * @param int $level
1193 * @return array best mask
1194 */
1195 protected function mask($width, $frame, $level) {
1196 $minDemerit = PHP_INT_MAX;
1197 $bestMaskNum = 0;
1198 $bestMask = array();
1199 $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
1200 if (QR_FIND_FROM_RANDOM !== false) {
1201 $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9);
1202 for ($i = 0; $i < $howManuOut; ++$i) {
1203 $remPos = rand (0, count($checked_masks)-1);
1204 unset($checked_masks[$remPos]);
1205 $checked_masks = array_values($checked_masks);
1206 }
1207 }
1208 $bestMask = $frame;
1209 foreach ($checked_masks as $i) {
1210 $mask = array_fill(0, $width, str_repeat("\0", $width));
1211 $demerit = 0;
1212 $blacks = 0;
1213 $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
1214 $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
1215 $blacks = (int)(100 * $blacks / ($width * $width));
1216 $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
1217 $demerit += $this->evaluateSymbol($width, $mask);
1218 if ($demerit < $minDemerit) {
1219 $minDemerit = $demerit;
1220 $bestMask = $mask;
1221 $bestMaskNum = $i;
1222 }
1223 }
1224 return $bestMask;
1225 }
1226
1227 // - - - - - - - - - - - - - - - - - - - - - - - - -
1228
1229 // QRsplit
1230
1231 /**
1232 * Return true if the character at specified position is a number
1233 * @param string $str string
1234 * @param int $pos characted position
1235 * @return boolean true of false
1236 */
1237 protected function isdigitat($str, $pos) {
1238 if ($pos >= strlen($str)) {
1239 return false;
1240 }
1241 return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
1242 }
1243
1244 /**
1245 * Return true if the character at specified position is an alphanumeric character
1246 * @param string $str string
1247 * @param int $pos characted position
1248 * @return boolean true of false
1249 */
1250 protected function isalnumat($str, $pos) {
1251 if ($pos >= strlen($str)) {
1252 return false;
1253 }
1254 return ($this->lookAnTable(ord($str[$pos])) >= 0);
1255 }
1256
1257 /**
1258 * identifyMode
1259 * @param int $pos
1260 * @return int mode
1261 */
1262 protected function identifyMode($pos) {
1263 if ($pos >= strlen($this->dataStr)) {
1264 return QR_MODE_NL;
1265 }
1266 $c = $this->dataStr[$pos];
1267 if ($this->isdigitat($this->dataStr, $pos)) {
1268 return QR_MODE_NM;
1269 } elseif ($this->isalnumat($this->dataStr, $pos)) {
1270 return QR_MODE_AN;
1271 } elseif ($this->hint == QR_MODE_KJ) {
1272 if ($pos+1 < strlen($this->dataStr)) {
1273 $d = $this->dataStr[$pos+1];
1274 $word = (ord($c) << 8) | ord($d);
1275 if (($word >= 0x8140 && $word <= 0x9ffc) OR ($word >= 0xe040 && $word <= 0xebbf)) {
1276 return QR_MODE_KJ;
1277 }
1278 }
1279 }
1280 return QR_MODE_8B;
1281 }
1282
1283 /**
1284 * eatNum
1285 * @return int run
1286 */
1287 protected function eatNum() {
1288 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1289 $p = 0;
1290 while($this->isdigitat($this->dataStr, $p)) {
1291 $p++;
1292 }
1293 $run = $p;
1294 $mode = $this->identifyMode($p);
1295 if ($mode == QR_MODE_8B) {
1296 $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1297 + $this->estimateBitsMode8(1) // + 4 + l8
1298 - $this->estimateBitsMode8($run + 1); // - 4 - l8
1299 if ($dif > 0) {
1300 return $this->eat8();
1301 }
1302 }
1303 if ($mode == QR_MODE_AN) {
1304 $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1305 + $this->estimateBitsModeAn(1) // + 4 + la
1306 - $this->estimateBitsModeAn($run + 1);// - 4 - la
1307 if ($dif > 0) {
1308 return $this->eatAn();
1309 }
1310 }
1311 $this->items = $this->appendNewInputItem($this->items, QR_MODE_NM, $run, str_split($this->dataStr));
1312 return $run;
1313 }
1314
1315 /**
1316 * eatAn
1317 * @return int run
1318 */
1319 protected function eatAn() {
1320 $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
1321 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1322 $p =1 ;
1323 while($this->isalnumat($this->dataStr, $p)) {
1324 if ($this->isdigitat($this->dataStr, $p)) {
1325 $q = $p;
1326 while($this->isdigitat($this->dataStr, $q)) {
1327 $q++;
1328 }
1329 $dif = $this->estimateBitsModeAn($p) // + 4 + la
1330 + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1331 - $this->estimateBitsModeAn($q); // - 4 - la
1332 if ($dif < 0) {
1333 break;
1334 } else {
1335 $p = $q;
1336 }
1337 } else {
1338 $p++;
1339 }
1340 }
1341 $run = $p;
1342 if (!$this->isalnumat($this->dataStr, $p)) {
1343 $dif = $this->estimateBitsModeAn($run) + 4 + $la
1344 + $this->estimateBitsMode8(1) // + 4 + l8
1345 - $this->estimateBitsMode8($run + 1); // - 4 - l8
1346 if ($dif > 0) {
1347 return $this->eat8();
1348 }
1349 }
1350 $this->items = $this->appendNewInputItem($this->items, QR_MODE_AN, $run, str_split($this->dataStr));
1351 return $run;
1352 }
1353
1354 /**
1355 * eatKanji
1356 * @return int run
1357 */
1358 protected function eatKanji() {
1359 $p = 0;
1360 while($this->identifyMode($p) == QR_MODE_KJ) {
1361 $p += 2;
1362 }
1363 $this->items = $this->appendNewInputItem($this->items, QR_MODE_KJ, $p, str_split($this->dataStr));
1364 $run = $p;
1365 return $run;
1366 }
1367
1368 /**
1369 * eat8
1370 * @return int run
1371 */
1372 protected function eat8() {
1373 $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
1374 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1375 $p = 1;
1376 $dataStrLen = strlen($this->dataStr);
1377 while($p < $dataStrLen) {
1378 $mode = $this->identifyMode($p);
1379 if ($mode == QR_MODE_KJ) {
1380 break;
1381 }
1382 if ($mode == QR_MODE_NM) {
1383 $q = $p;
1384 while($this->isdigitat($this->dataStr, $q)) {
1385 $q++;
1386 }
1387 $dif = $this->estimateBitsMode8($p) // + 4 + l8
1388 + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1389 - $this->estimateBitsMode8($q); // - 4 - l8
1390 if ($dif < 0) {
1391 break;
1392 } else {
1393 $p = $q;
1394 }
1395 } elseif ($mode == QR_MODE_AN) {
1396 $q = $p;
1397 while($this->isalnumat($this->dataStr, $q)) {
1398 $q++;
1399 }
1400 $dif = $this->estimateBitsMode8($p) // + 4 + l8
1401 + $this->estimateBitsModeAn($q - $p) + 4 + $la
1402 - $this->estimateBitsMode8($q); // - 4 - l8
1403 if ($dif < 0) {
1404 break;
1405 } else {
1406 $p = $q;
1407 }
1408 } else {
1409 $p++;
1410 }
1411 }
1412 $run = $p;
1413 $this->items = $this->appendNewInputItem($this->items, QR_MODE_8B, $run, str_split($this->dataStr));
1414 return $run;
1415 }
1416
1417 /**
1418 * splitString
1419 * @return int
1420 */
1421 protected function splitString() {
1422 while (strlen($this->dataStr) > 0) {
1423 $mode = $this->identifyMode(0);
1424 switch ($mode) {
1425 case QR_MODE_NM: {
1426 $length = $this->eatNum();
1427 break;
1428 }
1429 case QR_MODE_AN: {
1430 $length = $this->eatAn();
1431 break;
1432 }
1433 case QR_MODE_KJ: {
1434 if ($this->hint == QR_MODE_KJ) {
1435 $length = $this->eatKanji();
1436 } else {
1437 $length = $this->eat8();
1438 }
1439 break;
1440 }
1441 default: {
1442 $length = $this->eat8();
1443 break;
1444 }
1445 }
1446 if ($length == 0) {
1447 return 0;
1448 }
1449 if ($length < 0) {
1450 return -1;
1451 }
1452 $this->dataStr = substr($this->dataStr, $length);
1453 }
1454 return 0;
1455 }
1456
1457 /**
1458 * toUpper
1459 */
1460 protected function toUpper() {
1461 $stringLen = strlen($this->dataStr);
1462 $p = 0;
1463 while ($p < $stringLen) {
1464 $mode = $this->identifyMode(substr($this->dataStr, $p));
1465 if ($mode == QR_MODE_KJ) {
1466 $p += 2;
1467 } else {
1468 if ((ord($this->dataStr[$p]) >= ord('a')) AND (ord($this->dataStr[$p]) <= ord('z'))) {
1469 $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
1470 }
1471 $p++;
1472 }
1473 }
1474 return $this->dataStr;
1475 }
1476
1477 // - - - - - - - - - - - - - - - - - - - - - - - - -
1478
1479 // QRinputItem
1480
1481 /**
1482 * newInputItem
1483 * @param int $mode
1484 * @param int $size
1485 * @param array $data
1486 * @param array $bstream
1487 * @return array input item
1488 */
1489 protected function newInputItem($mode, $size, $data, $bstream=null) {
1490 $setData = array_slice($data, 0, $size);
1491 if (count($setData) < $size) {
1492 $setData = array_merge($setData, array_fill(0, ($size - count($setData)), 0));
1493 }
1494 if (!$this->check($mode, $size, $setData)) {
1495 return NULL;
1496 }
1497 $inputitem = array();
1498 $inputitem['mode'] = $mode;
1499 $inputitem['size'] = $size;
1500 $inputitem['data'] = $setData;
1501 $inputitem['bstream'] = $bstream;
1502 return $inputitem;
1503 }
1504
1505 /**
1506 * encodeModeNum
1507 * @param array $inputitem
1508 * @param int $version
1509 * @return array input item
1510 */
1511 protected function encodeModeNum($inputitem, $version) {
1512 $words = (int)($inputitem['size'] / 3);
1513 $inputitem['bstream'] = array();
1514 $val = 0x1;
1515 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1516 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_NM, $version), $inputitem['size']);
1517 for ($i=0; $i < $words; ++$i) {
1518 $val = (ord($inputitem['data'][$i*3 ]) - ord('0')) * 100;
1519 $val += (ord($inputitem['data'][$i*3+1]) - ord('0')) * 10;
1520 $val += (ord($inputitem['data'][$i*3+2]) - ord('0'));
1521 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
1522 }
1523 if ($inputitem['size'] - $words * 3 == 1) {
1524 $val = ord($inputitem['data'][$words*3]) - ord('0');
1525 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1526 } elseif (($inputitem['size'] - ($words * 3)) == 2) {
1527 $val = (ord($inputitem['data'][$words*3 ]) - ord('0')) * 10;
1528 $val += (ord($inputitem['data'][$words*3+1]) - ord('0'));
1529 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
1530 }
1531 return $inputitem;
1532 }
1533
1534 /**
1535 * encodeModeAn
1536 * @param array $inputitem
1537 * @param int $version
1538 * @return array input item
1539 */
1540 protected function encodeModeAn($inputitem, $version) {
1541 $words = (int)($inputitem['size'] / 2);
1542 $inputitem['bstream'] = array();
1543 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02);
1544 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_AN, $version), $inputitem['size']);
1545 for ($i=0; $i < $words; ++$i) {
1546 $val = (int)($this->lookAnTable(ord($inputitem['data'][$i*2])) * 45);
1547 $val += (int)($this->lookAnTable(ord($inputitem['data'][($i*2)+1])));
1548 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
1549 }
1550 if ($inputitem['size'] & 1) {
1551 $val = $this->lookAnTable(ord($inputitem['data'][($words * 2)]));
1552 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
1553 }
1554 return $inputitem;
1555 }
1556
1557 /**
1558 * encodeMode8
1559 * @param array $inputitem
1560 * @param int $version
1561 * @return array input item
1562 */
1563 protected function encodeMode8($inputitem, $version) {
1564 $inputitem['bstream'] = array();
1565 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
1566 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_8B, $version), $inputitem['size']);
1567 for ($i=0; $i < $inputitem['size']; ++$i) {
1568 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][$i]));
1569 }
1570 return $inputitem;
1571 }
1572
1573 /**
1574 * encodeModeKanji
1575 * @param array $inputitem
1576 * @param int $version
1577 * @return array input item
1578 */
1579 protected function encodeModeKanji($inputitem, $version) {
1580 $inputitem['bstream'] = array();
1581 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
1582 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_KJ, $version), (int)($inputitem['size'] / 2));
1583 for ($i=0; $i<$inputitem['size']; $i+=2) {
1584 $val = (ord($inputitem['data'][$i]) << 8) | ord($inputitem['data'][$i+1]);
1585 if ($val <= 0x9ffc) {
1586 $val -= 0x8140;
1587 } else {
1588 $val -= 0xc140;
1589 }
1590 $h = ($val >> 8) * 0xc0;
1591 $val = ($val & 0xff) + $h;
1592 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
1593 }
1594 return $inputitem;
1595 }
1596
1597 /**
1598 * encodeModeStructure
1599 * @param array $inputitem
1600 * @return array input item
1601 */
1602 protected function encodeModeStructure($inputitem) {
1603 $inputitem['bstream'] = array();
1604 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03);
1605 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][1]) - 1);
1606 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][0]) - 1);
1607 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][2]));
1608 return $inputitem;
1609 }
1610
1611 /**
1612 * encodeBitStream
1613 * @param array $inputitem
1614 * @param int $version
1615 * @return array input item
1616 */
1617 protected function encodeBitStream($inputitem, $version) {
1618 $inputitem['bstream'] = array();
1619 $words = $this->maximumWords($inputitem['mode'], $version);
1620 if ($inputitem['size'] > $words) {
1621 $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
1622 $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, array_slice($inputitem['data'], $words));
1623 $st1 = $this->encodeBitStream($st1, $version);
1624 $st2 = $this->encodeBitStream($st2, $version);
1625 $inputitem['bstream'] = array();
1626 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
1627 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
1628 } else {
1629 switch($inputitem['mode']) {
1630 case QR_MODE_NM: {
1631 $inputitem = $this->encodeModeNum($inputitem, $version);
1632 break;
1633 }
1634 case QR_MODE_AN: {
1635 $inputitem = $this->encodeModeAn($inputitem, $version);
1636 break;
1637 }
1638 case QR_MODE_8B: {
1639 $inputitem = $this->encodeMode8($inputitem, $version);
1640 break;
1641 }
1642 case QR_MODE_KJ: {
1643 $inputitem = $this->encodeModeKanji($inputitem, $version);
1644 break;
1645 }
1646 case QR_MODE_ST: {
1647 $inputitem = $this->encodeModeStructure($inputitem);
1648 break;
1649 }
1650 default: {
1651 break;
1652 }
1653 }
1654 }
1655 return $inputitem;
1656 }
1657
1658 // - - - - - - - - - - - - - - - - - - - - - - - - -
1659
1660 // QRinput
1661
1662 /**
1663 * Append data to an input object.
1664 * The data is copied and appended to the input object.
1665 * @param array $items input items
1666 * @param int $mode encoding mode.
1667 * @param int $size size of data (byte).
1668 * @param array $data array of input data.
1669 * @return array items
1670 *
1671 */
1672 protected function appendNewInputItem($items, $mode, $size, $data) {
1673 $newitem = $this->newInputItem($mode, $size, $data);
1674 if (!empty($newitem)) {
1675 $items[] = $newitem;
1676 }
1677 return $items;
1678 }
1679
1680 /**
1681 * insertStructuredAppendHeader
1682 * @param array $items
1683 * @param int $size
1684 * @param int $index
1685 * @param int $parity
1686 * @return array items
1687 */
1688 protected function insertStructuredAppendHeader($items, $size, $index, $parity) {
1689 if ($size > MAX_STRUCTURED_SYMBOLS) {
1690 return -1;
1691 }
1692 if (($index <= 0) OR ($index > MAX_STRUCTURED_SYMBOLS)) {
1693 return -1;
1694 }
1695 $buf = array($size, $index, $parity);
1696 $entry = $this->newInputItem(QR_MODE_ST, 3, $buf);
1697 array_unshift($items, $entry);
1698 return $items;
1699 }
1700
1701 /**
1702 * calcParity
1703 * @param array $items
1704 * @return int parity
1705 */
1706 protected function calcParity($items) {
1707 $parity = 0;
1708 foreach ($items as $item) {
1709 if ($item['mode'] != QR_MODE_ST) {
1710 for ($i=$item['size']-1; $i>=0; --$i) {
1711 $parity ^= $item['data'][$i];
1712 }
1713 }
1714 }
1715 return $parity;
1716 }
1717
1718 /**
1719 * checkModeNum
1720 * @param int $size
1721 * @param array $data
1722 * @return boolean true or false
1723 */
1724 protected function checkModeNum($size, $data) {
1725 for ($i=0; $i<$size; ++$i) {
1726 if ((ord($data[$i]) < ord('0')) OR (ord($data[$i]) > ord('9'))){
1727 return false;
1728 }
1729 }
1730 return true;
1731 }
1732
1733 /**
1734 * Look up the alphabet-numeric conversion table (see JIS X0510:2004, pp.19).
1735 * @param int $c character value
1736 * @return int value
1737 */
1738 protected function lookAnTable($c) {
1739 return (($c > 127)?-1:$this->anTable[$c]);
1740 }
1741
1742 /**
1743 * checkModeAn
1744 * @param int $size
1745 * @param array $data
1746 * @return boolean true or false
1747 */
1748 protected function checkModeAn($size, $data) {
1749 for ($i=0; $i<$size; ++$i) {
1750 if ($this->lookAnTable(ord($data[$i])) == -1) {
1751 return false;
1752 }
1753 }
1754 return true;
1755 }
1756
1757 /**
1758 * estimateBitsModeNum
1759 * @param int $size
1760 * @return int number of bits
1761 */
1762 protected function estimateBitsModeNum($size) {
1763 $w = (int)($size / 3);
1764 $bits = ($w * 10);
1765 switch($size - ($w * 3)) {
1766 case 1: {
1767 $bits += 4;
1768 break;
1769 }
1770 case 2: {
1771 $bits += 7;
1772 break;
1773 }
1774 }
1775 return $bits;
1776 }
1777
1778 /**
1779 * estimateBitsModeAn
1780 * @param int $size
1781 * @return int number of bits
1782 */
1783 protected function estimateBitsModeAn($size) {
1784 $bits = (int)($size * 5.5); // (size / 2 ) * 11
1785 if ($size & 1) {
1786 $bits += 6;
1787 }
1788 return $bits;
1789 }
1790
1791 /**
1792 * estimateBitsMode8
1793 * @param int $size
1794 * @return int number of bits
1795 */
1796 protected function estimateBitsMode8($size) {
1797 return (int)($size * 8);
1798 }
1799
1800 /**
1801 * estimateBitsModeKanji
1802 * @param int $size
1803 * @return int number of bits
1804 */
1805 protected function estimateBitsModeKanji($size) {
1806 return (int)($size * 6.5); // (size / 2 ) * 13
1807 }
1808
1809 /**
1810 * checkModeKanji
1811 * @param int $size
1812 * @param array $data
1813 * @return boolean true or false
1814 */
1815 protected function checkModeKanji($size, $data) {
1816 if ($size & 1) {
1817 return false;
1818 }
1819 for ($i=0; $i<$size; $i+=2) {
1820 $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
1821 if (($val < 0x8140) OR (($val > 0x9ffc) AND ($val < 0xe040)) OR ($val > 0xebbf)) {
1822 return false;
1823 }
1824 }
1825 return true;
1826 }
1827
1828 /**
1829 * Validate the input data.
1830 * @param int $mode encoding mode.
1831 * @param int $size size of data (byte).
1832 * @param array $data data to validate
1833 * @return boolean true in case of valid data, false otherwise
1834 */
1835 protected function check($mode, $size, $data) {
1836 if ($size <= 0) {
1837 return false;
1838 }
1839 switch($mode) {
1840 case QR_MODE_NM: {
1841 return $this->checkModeNum($size, $data);
1842 }
1843 case QR_MODE_AN: {
1844 return $this->checkModeAn($size, $data);
1845 }
1846 case QR_MODE_KJ: {
1847 return $this->checkModeKanji($size, $data);
1848 }
1849 case QR_MODE_8B: {
1850 return true;
1851 }
1852 case QR_MODE_ST: {
1853 return true;
1854 }
1855 default: {
1856 break;
1857 }
1858 }
1859 return false;
1860 }
1861
1862 /**
1863 * estimateBitStreamSize
1864 * @param array $items
1865 * @param int $version
1866 * @return int bits
1867 */
1868 protected function estimateBitStreamSize($items, $version) {
1869 $bits = 0;
1870 if ($version == 0) {
1871 $version = 1;
1872 }
1873 foreach ($items as $item) {
1874 switch($item['mode']) {
1875 case QR_MODE_NM: {
1876 $bits = $this->estimateBitsModeNum($item['size']);
1877 break;
1878 }
1879 case QR_MODE_AN: {
1880 $bits = $this->estimateBitsModeAn($item['size']);
1881 break;
1882 }
1883 case QR_MODE_8B: {
1884 $bits = $this->estimateBitsMode8($item['size']);
1885 break;
1886 }
1887 case QR_MODE_KJ: {
1888 $bits = $this->estimateBitsModeKanji($item['size']);
1889 break;
1890 }
1891 case QR_MODE_ST: {
1892 return STRUCTURE_HEADER_BITS;
1893 }
1894 default: {
1895 return 0;
1896 }
1897 }
1898 $l = $this->lengthIndicator($item['mode'], $version);
1899 $m = 1 << $l;
1900 $num = (int)(($item['size'] + $m - 1) / $m);
1901 $bits += $num * (4 + $l);
1902 }
1903 return $bits;
1904 }
1905
1906 /**
1907 * estimateVersion
1908 * @param array $items
1909 * @return int version
1910 */
1911 protected function estimateVersion($items) {
1912 $version = 0;
1913 $prev = 0;
1914 do {
1915 $prev = $version;
1916 $bits = $this->estimateBitStreamSize($items, $prev);
1917 $version = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
1918 if ($version < 0) {
1919 return -1;
1920 }
1921 } while ($version > $prev);
1922 return $version;
1923 }
1924
1925 /**
1926 * lengthOfCode
1927 * @param int $mode
1928 * @param int $version
1929 * @param int $bits
1930 * @return int size
1931 */
1932 protected function lengthOfCode($mode, $version, $bits) {
1933 $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
1934 switch($mode) {
1935 case QR_MODE_NM: {
1936 $chunks = (int)($payload / 10);
1937 $remain = $payload - $chunks * 10;
1938 $size = $chunks * 3;
1939 if ($remain >= 7) {
1940 $size += 2;
1941 } elseif ($remain >= 4) {
1942 $size += 1;
1943 }
1944 break;
1945 }
1946 case QR_MODE_AN: {
1947 $chunks = (int)($payload / 11);
1948 $remain = $payload - $chunks * 11;
1949 $size = $chunks * 2;
1950 if ($remain >= 6) {
1951 ++$size;
1952 }
1953 break;
1954 }
1955 case QR_MODE_8B: {
1956 $size = (int)($payload / 8);
1957 break;
1958 }
1959 case QR_MODE_KJ: {
1960 $size = (int)(($payload / 13) * 2);
1961 break;
1962 }
1963 case QR_MODE_ST: {
1964 $size = (int)($payload / 8);
1965 break;
1966 }
1967 default: {
1968 $size = 0;
1969 break;
1970 }
1971 }
1972 $maxsize = $this->maximumWords($mode, $version);
1973 if ($size < 0) {
1974 $size = 0;
1975 }
1976 if ($size > $maxsize) {
1977 $size = $maxsize;
1978 }
1979 return $size;
1980 }
1981
1982 /**
1983 * createBitStream
1984 * @param array $items
1985 * @return array of items and total bits
1986 */
1987 protected function createBitStream($items) {
1988 $total = 0;
1989 foreach ($items as $key => $item) {
1990 $items[$key] = $this->encodeBitStream($item, $this->version);
1991 $bits = count($items[$key]['bstream']);
1992 $total += $bits;
1993 }
1994 return array($items, $total);
1995 }
1996
1997 /**
1998 * convertData
1999 * @param array $items
2000 * @return array items
2001 */
2002 protected function convertData($items) {
2003 $ver = $this->estimateVersion($items);
2004 if ($ver > $this->version) {
2005 $this->version = $ver;
2006 }
2007 while (true) {
2008 $cbs = $this->createBitStream($items);
2009 $items = $cbs[0];
2010 $bits = $cbs[1];
2011 if ($bits < 0) {
2012 return -1;
2013 }
2014 $ver = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
2015 if ($ver < 0) {
2016 return -1;
2017 } elseif ($ver > $this->version) {
2018 $this->version = $ver;
2019 } else {
2020 break;
2021 }
2022 }
2023 return $items;
2024 }
2025
2026 /**
2027 * Append Padding Bit to bitstream
2028 * @param array $bstream
2029 * @return array bitstream
2030 */
2031 protected function appendPaddingBit($bstream) {
2032 if (is_null($bstream)) {
2033 return null;
2034 }
2035 $bits = count($bstream);
2036 $maxwords = $this->getDataLength($this->version, $this->level);
2037 $maxbits = $maxwords * 8;
2038 if ($maxbits == $bits) {
2039 return $bstream;
2040 }
2041 if ($maxbits - $bits < 5) {
2042 return $this->appendNum($bstream, $maxbits - $bits, 0);
2043 }
2044 $bits += 4;
2045 $words = (int)(($bits + 7) / 8);
2046 $padding = array();
2047 $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
2048 $padlen = $maxwords - $words;
2049 if ($padlen > 0) {
2050 $padbuf = array();
2051 for ($i=0; $i<$padlen; ++$i) {
2052 $padbuf[$i] = ($i&1)?0x11:0xec;
2053 }
2054 $padding = $this->appendBytes($padding, $padlen, $padbuf);
2055 }
2056 return $this->appendBitstream($bstream, $padding);
2057 }
2058
2059 /**
2060 * mergeBitStream
2061 * @param array $items items
2062 * @return array bitstream
2063 */
2064 protected function mergeBitStream($items) {
2065 $items = $this->convertData($items);
2066 if (!is_array($items)) {
2067 return null;
2068 }
2069 $bstream = array();
2070 foreach ($items as $item) {
2071 $bstream = $this->appendBitstream($bstream, $item['bstream']);
2072 }
2073 return $bstream;
2074 }
2075
2076 /**
2077 * Returns a stream of bits.
2078 * @param int $items
2079 * @return array padded merged byte stream
2080 */
2081 protected function getBitStream($items) {
2082 $bstream = $this->mergeBitStream($items);
2083 return $this->appendPaddingBit($bstream);
2084 }
2085
2086 /**
2087 * Pack all bit streams padding bits into a byte array.
2088 * @param int $items
2089 * @return array padded merged byte stream
2090 */
2091 protected function getByteStream($items) {
2092 $bstream = $this->getBitStream($items);
2093 return $this->bitstreamToByte($bstream);
2094 }
2095
2096 // - - - - - - - - - - - - - - - - - - - - - - - - -
2097
2098 // QRbitstream
2099
2100 /**
2101 * Return an array with zeros
2102 * @param int $setLength array size
2103 * @return array
2104 */
2105 protected function allocate($setLength) {
2106 return array_fill(0, $setLength, 0);
2107 }
2108
2109 /**
2110 * Return new bitstream from number
2111 * @param int $bits number of bits
2112 * @param int $num number
2113 * @return array bitstream
2114 */
2115 protected function newFromNum($bits, $num) {
2116 $bstream = $this->allocate($bits);
2117 $mask = 1 << ($bits - 1);
2118 for ($i=0; $i<$bits; ++$i) {
2119 if ($num & $mask) {
2120 $bstream[$i] = 1;
2121 } else {
2122 $bstream[$i] = 0;
2123 }
2124 $mask = $mask >> 1;
2125 }
2126 return $bstream;
2127 }
2128
2129 /**
2130 * Return new bitstream from bytes
2131 * @param int $size size
2132 * @param array $data bytes
2133 * @return array bitstream
2134 */
2135 protected function newFromBytes($size, $data) {
2136 $bstream = $this->allocate($size * 8);
2137 $p=0;
2138 for ($i=0; $i<$size; ++$i) {
2139 $mask = 0x80;
2140 for ($j=0; $j<8; ++$j) {
2141 if ($data[$i] & $mask) {
2142 $bstream[$p] = 1;
2143 } else {
2144 $bstream[$p] = 0;
2145 }
2146 $p++;
2147 $mask = $mask >> 1;
2148 }
2149 }
2150 return $bstream;
2151 }
2152
2153 /**
2154 * Append one bitstream to another
2155 * @param array $bitstream original bitstream
2156 * @param array $append bitstream to append
2157 * @return array bitstream
2158 */
2159 protected function appendBitstream($bitstream, $append) {
2160 if ((!is_array($append)) OR (count($append) == 0)) {
2161 return $bitstream;
2162 }
2163 if (count($bitstream) == 0) {
2164 return $append;
2165 }
2166 return array_values(array_merge($bitstream, $append));
2167 }
2168
2169 /**
2170 * Append one bitstream created from number to another
2171 * @param array $bitstream original bitstream
2172 * @param int $bits number of bits
2173 * @param int $num number
2174 * @return array bitstream
2175 */
2176 protected function appendNum($bitstream, $bits, $num) {
2177 if ($bits == 0) {
2178 return 0;
2179 }
2180 $b = $this->newFromNum($bits, $num);
2181 return $this->appendBitstream($bitstream, $b);
2182 }
2183
2184 /**
2185 * Append one bitstream created from bytes to another
2186 * @param array $bitstream original bitstream
2187 * @param int $size size
2188 * @param array $data bytes
2189 * @return array bitstream
2190 */
2191 protected function appendBytes($bitstream, $size, $data) {
2192 if ($size == 0) {
2193 return 0;
2194 }
2195 $b = $this->newFromBytes($size, $data);
2196 return $this->appendBitstream($bitstream, $b);
2197 }
2198
2199 /**
2200 * Convert bitstream to bytes
2201 * @param array $bstream original bitstream
2202 * @return array of bytes
2203 */
2204 protected function bitstreamToByte($bstream) {
2205 if (is_null($bstream)) {
2206 return null;
2207 }
2208 $size = count($bstream);
2209 if ($size == 0) {
2210 return array();
2211 }
2212 $data = array_fill(0, (int)(($size + 7) / 8), 0);
2213 $bytes = (int)($size / 8);
2214 $p = 0;
2215 for ($i=0; $i<$bytes; $i++) {
2216 $v = 0;
2217 for ($j=0; $j<8; $j++) {
2218 $v = $v << 1;
2219 $v |= $bstream[$p];
2220 $p++;
2221 }
2222 $data[$i] = $v;
2223 }
2224 if ($size & 7) {
2225 $v = 0;
2226 for ($j=0; $j<($size & 7); $j++) {
2227 $v = $v << 1;
2228 $v |= $bstream[$p];
2229 $p++;
2230 }
2231 $data[$bytes] = $v;
2232 }
2233 return $data;
2234 }
2235
2236 // - - - - - - - - - - - - - - - - - - - - - - - - -
2237
2238 // QRspec
2239
2240 /**
2241 * Replace a value on the array at the specified position
2242 * @param array $srctab
2243 * @param int $x X position
2244 * @param int $y Y position
2245 * @param string $repl value to replace
2246 * @param int $replLen length of the repl string
2247 * @return array srctab
2248 */
2249 protected function qrstrset($srctab, $x, $y, $repl, $replLen=false) {
2250 $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
2251 return $srctab;
2252 }
2253
2254 /**
2255 * Return maximum data code length (bytes) for the version.
2256 * @param int $version version
2257 * @param int $level error correction level
2258 * @return int maximum size (bytes)
2259 */
2260 protected function getDataLength($version, $level) {
2261 return $this->capacity[$version][QRCAP_WORDS] - $this->capacity[$version][QRCAP_EC][$level];
2262 }
2263
2264 /**
2265 * Return maximum error correction code length (bytes) for the version.
2266 * @param int $version version
2267 * @param int $level error correction level
2268 * @return int ECC size (bytes)
2269 */
2270 protected function getECCLength($version, $level){
2271 return $this->capacity[$version][QRCAP_EC][$level];
2272 }
2273
2274 /**
2275 * Return the width of the symbol for the version.
2276 * @param int $version version
2277 * @return int width
2278 */
2279 protected function getWidth($version) {
2280 return $this->capacity[$version][QRCAP_WIDTH];
2281 }
2282
2283 /**
2284 * Return the numer of remainder bits.
2285 * @param int $version version
2286 * @return int number of remainder bits
2287 */
2288 protected function getRemainder($version) {
2289 return $this->capacity[$version][QRCAP_REMINDER];
2290 }
2291
2292 /**
2293 * Return a version number that satisfies the input code length.
2294 * @param int $size input code length (bytes)
2295 * @param int $level error correction level
2296 * @return int version number
2297 */
2298 protected function getMinimumVersion($size, $level) {
2299 for ($i = 1; $i <= QRSPEC_VERSION_MAX; ++$i) {
2300 $words = ($this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]);
2301 if ($words >= $size) {
2302 return $i;
2303 }
2304 }
2305 // the size of input data is greater than QR capacity, try to lover the error correction mode
2306 return -1;
2307 }
2308
2309 /**
2310 * Return the size of length indicator for the mode and version.
2311 * @param int $mode encoding mode
2312 * @param int $version version
2313 * @return int the size of the appropriate length indicator (bits).
2314 */
2315 protected function lengthIndicator($mode, $version) {
2316 if ($mode == QR_MODE_ST) {
2317 return 0;
2318 }
2319 if ($version <= 9) {
2320 $l = 0;
2321 } elseif ($version <= 26) {
2322 $l = 1;
2323 } else {
2324 $l = 2;
2325 }
2326 return $this->lengthTableBits[$mode][$l];
2327 }
2328
2329 /**
2330 * Return the maximum length for the mode and version.
2331 * @param int $mode encoding mode
2332 * @param int $version version
2333 * @return int the maximum length (bytes)
2334 */
2335 protected function maximumWords($mode, $version) {
2336 if ($mode == QR_MODE_ST) {
2337 return 3;
2338 }
2339 if ($version <= 9) {
2340 $l = 0;
2341 } else if ($version <= 26) {
2342 $l = 1;
2343 } else {
2344 $l = 2;
2345 }
2346 $bits = $this->lengthTableBits[$mode][$l];
2347 $words = (1 << $bits) - 1;
2348 if ($mode == QR_MODE_KJ) {
2349 $words *= 2; // the number of bytes is required
2350 }
2351 return $words;
2352 }
2353
2354 /**
2355 * Return an array of ECC specification.
2356 * @param int $version version
2357 * @param int $level error correction level
2358 * @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}
2359 * @return array spec
2360 */
2361 protected function getEccSpec($version, $level, $spec) {
2362 if (count($spec) < 5) {
2363 $spec = array(0, 0, 0, 0, 0);
2364 }
2365 $b1 = $this->eccTable[$version][$level][0];
2366 $b2 = $this->eccTable[$version][$level][1];
2367 $data = $this->getDataLength($version, $level);
2368 $ecc = $this->getECCLength($version, $level);
2369 if ($b2 == 0) {
2370 $spec[0] = $b1;
2371 $spec[1] = (int)($data / $b1);
2372 $spec[2] = (int)($ecc / $b1);
2373 $spec[3] = 0;
2374 $spec[4] = 0;
2375 } else {
2376 $spec[0] = $b1;
2377 $spec[1] = (int)($data / ($b1 + $b2));
2378 $spec[2] = (int)($ecc / ($b1 + $b2));
2379 $spec[3] = $b2;
2380 $spec[4] = $spec[1] + 1;
2381 }
2382 return $spec;
2383 }
2384
2385 /**
2386 * Put an alignment marker.
2387 * @param array $frame frame
2388 * @param int $ox X center coordinate of the pattern
2389 * @param int $oy Y center coordinate of the pattern
2390 * @return array frame
2391 */
2392 protected function putAlignmentMarker($frame, $ox, $oy) {
2393 $finder = array(
2394 "\xa1\xa1\xa1\xa1\xa1",
2395 "\xa1\xa0\xa0\xa0\xa1",
2396 "\xa1\xa0\xa1\xa0\xa1",
2397 "\xa1\xa0\xa0\xa0\xa1",
2398 "\xa1\xa1\xa1\xa1\xa1"
2399 );
2400 $yStart = $oy - 2;
2401 $xStart = $ox - 2;
2402 for ($y=0; $y < 5; $y++) {
2403 $frame = $this->qrstrset($frame, $xStart, $yStart+$y, $finder[$y]);
2404 }
2405 return $frame;
2406 }
2407
2408 /**
2409 * Put an alignment pattern.
2410 * @param int $version version
2411 * @param array $frame frame
2412 * @param int $width width
2413 * @return array frame
2414 */
2415 protected function putAlignmentPattern($version, $frame, $width) {
2416 if ($version < 2) {
2417 return $frame;
2418 }
2419 $d = $this->alignmentPattern[$version][1] - $this->alignmentPattern[$version][0];
2420 if ($d < 0) {
2421 $w = 2;
2422 } else {
2423 $w = (int)(($width - $this->alignmentPattern[$version][0]) / $d + 2);
2424 }
2425 if ($w * $w - 3 == 1) {
2426 $x = $this->alignmentPattern[$version][0];
2427 $y = $this->alignmentPattern[$version][0];
2428 $frame = $this->putAlignmentMarker($frame, $x, $y);
2429 return $frame;
2430 }
2431 $cx = $this->alignmentPattern[$version][0];
2432 $wo = $w - 1;
2433 for ($x=1; $x < $wo; ++$x) {
2434 $frame = $this->putAlignmentMarker($frame, 6, $cx);
2435 $frame = $this->putAlignmentMarker($frame, $cx, 6);
2436 $cx += $d;
2437 }
2438 $cy = $this->alignmentPattern[$version][0];
2439 for ($y=0; $y < $wo; ++$y) {
2440 $cx = $this->alignmentPattern[$version][0];
2441 for ($x=0; $x < $wo; ++$x) {
2442 $frame = $this->putAlignmentMarker($frame, $cx, $cy);
2443 $cx += $d;
2444 }
2445 $cy += $d;
2446 }
2447 return $frame;
2448 }
2449
2450 /**
2451 * Return BCH encoded version information pattern that is used for the symbol of version 7 or greater. Use lower 18 bits.
2452 * @param int $version version
2453 * @return string BCH encoded version information pattern
2454 */
2455 protected function getVersionPattern($version) {
2456 if (($version < 7) OR ($version > QRSPEC_VERSION_MAX)) {
2457 return 0;
2458 }
2459 return $this->versionPattern[($version - 7)];
2460 }
2461
2462 /**
2463 * Return BCH encoded format information pattern.
2464 * @param array $mask
2465 * @param int $level error correction level
2466 * @return string BCH encoded format information pattern
2467 */
2468 protected function getFormatInfo($mask, $level) {
2469 if (($mask < 0) OR ($mask > 7)) {
2470 return 0;
2471 }
2472 if (($level < 0) OR ($level > 3)) {
2473 return 0;
2474 }
2475 return $this->formatInfo[$level][$mask];
2476 }
2477
2478 /**
2479 * Put a finder pattern.
2480 * @param array $frame frame
2481 * @param int $ox X center coordinate of the pattern
2482 * @param int $oy Y center coordinate of the pattern
2483 * @return array frame
2484 */
2485 protected function putFinderPattern($frame, $ox, $oy) {
2486 $finder = array(
2487 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
2488 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
2489 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2490 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2491 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2492 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
2493 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
2494 );
2495 for ($y=0; $y < 7; $y++) {
2496 $frame = $this->qrstrset($frame, $ox, ($oy + $y), $finder[$y]);
2497 }
2498 return $frame;
2499 }
2500
2501 /**
2502 * Return a copy of initialized frame.
2503 * @param int $version version
2504 * @return array array of unsigned char.
2505 */
2506 protected function createFrame($version) {
2507 $width = $this->capacity[$version][QRCAP_WIDTH];
2508 $frameLine = str_repeat ("\0", $width);
2509 $frame = array_fill(0, $width, $frameLine);
2510 // Finder pattern
2511 $frame = $this->putFinderPattern($frame, 0, 0);
2512 $frame = $this->putFinderPattern($frame, $width - 7, 0);
2513 $frame = $this->putFinderPattern($frame, 0, $width - 7);
2514 // Separator
2515 $yOffset = $width - 7;
2516 for ($y=0; $y < 7; ++$y) {
2517 $frame[$y][7] = "\xc0";
2518 $frame[$y][$width - 8] = "\xc0";
2519 $frame[$yOffset][7] = "\xc0";
2520 ++$yOffset;
2521 }
2522 $setPattern = str_repeat("\xc0", 8);
2523 $frame = $this->qrstrset($frame, 0, 7, $setPattern);
2524 $frame = $this->qrstrset($frame, $width-8, 7, $setPattern);
2525 $frame = $this->qrstrset($frame, 0, $width - 8, $setPattern);
2526 // Format info
2527 $setPattern = str_repeat("\x84", 9);
2528 $frame = $this->qrstrset($frame, 0, 8, $setPattern);
2529 $frame = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8);
2530 $yOffset = $width - 8;
2531 for ($y=0; $y < 8; ++$y,++$yOffset) {
2532 $frame[$y][8] = "\x84";
2533 $frame[$yOffset][8] = "\x84";
2534 }
2535 // Timing pattern
2536 $wo = $width - 15;
2537 for ($i=1; $i < $wo; ++$i) {
2538 $frame[6][7+$i] = chr(0x90 | ($i & 1));
2539 $frame[7+$i][6] = chr(0x90 | ($i & 1));
2540 }
2541 // Alignment pattern
2542 $frame = $this->putAlignmentPattern($version, $frame, $width);
2543 // Version information
2544 if ($version >= 7) {
2545 $vinf = $this->getVersionPattern($version);
2546 $v = $vinf;
2547 for ($x=0; $x<6; ++$x) {
2548 for ($y=0; $y<3; ++$y) {
2549 $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
2550 $v = $v >> 1;
2551 }
2552 }
2553 $v = $vinf;
2554 for ($y=0; $y<6; ++$y) {
2555 for ($x=0; $x<3; ++$x) {
2556 $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
2557 $v = $v >> 1;
2558 }
2559 }
2560 }
2561 // and a little bit...
2562 $frame[$width - 8][8] = "\x81";
2563 return $frame;
2564 }
2565
2566 /**
2567 * Set new frame for the specified version.
2568 * @param int $version version
2569 * @return array array of unsigned char.
2570 */
2571 protected function newFrame($version) {
2572 if (($version < 1) OR ($version > QRSPEC_VERSION_MAX)) {
2573 return NULL;
2574 }
2575 if (!isset($this->frames[$version])) {
2576 $this->frames[$version] = $this->createFrame($version);
2577 }
2578 if (is_null($this->frames[$version])) {
2579 return NULL;
2580 }
2581 return $this->frames[$version];
2582 }
2583
2584 /**
2585 * Return block number 0
2586 * @param array $spec
2587 * @return int value
2588 */
2589 protected function rsBlockNum($spec) {
2590 return ($spec[0] + $spec[3]);
2591 }
2592
2593 /**
2594 * Return block number 1
2595 * @param array $spec
2596 * @return int value
2597 */
2598 protected function rsBlockNum1($spec) {
2599 return $spec[0];
2600 }
2601
2602 /**
2603 * Return data codes 1
2604 * @param array $spec
2605 * @return int value
2606 */
2607 protected function rsDataCodes1($spec) {
2608 return $spec[1];
2609 }
2610
2611 /**
2612 * Return ecc codes 1
2613 * @param array $spec
2614 * @return int value
2615 */
2616 protected function rsEccCodes1($spec) {
2617 return $spec[2];
2618 }
2619
2620 /**
2621 * Return block number 2
2622 * @param array $spec
2623 * @return int value
2624 */
2625 protected function rsBlockNum2($spec) {
2626 return $spec[3];
2627 }
2628
2629 /**
2630 * Return data codes 2
2631 * @param array $spec
2632 * @return int value
2633 */
2634 protected function rsDataCodes2($spec) {
2635 return $spec[4];
2636 }
2637
2638 /**
2639 * Return ecc codes 2
2640 * @param array $spec
2641 * @return int value
2642 */
2643 protected function rsEccCodes2($spec) {
2644 return $spec[2];
2645 }
2646
2647 /**
2648 * Return data length
2649 * @param array $spec
2650 * @return int value
2651 */
2652 protected function rsDataLength($spec) {
2653 return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]);
2654 }
2655
2656 /**
2657 * Return ecc length
2658 * @param array $spec
2659 * @return int value
2660 */
2661 protected function rsEccLength($spec) {
2662 return ($spec[0] + $spec[3]) * $spec[2];
2663 }
2664
2665 // - - - - - - - - - - - - - - - - - - - - - - - - -
2666
2667 // QRrs
2668
2669 /**
2670 * Initialize a Reed-Solomon codec and add it to existing rsitems
2671 * @param int $symsize symbol size, bits
2672 * @param int $gfpoly Field generator polynomial coefficients
2673 * @param int $fcr first root of RS code generator polynomial, index form
2674 * @param int $prim primitive element to generate polynomial roots
2675 * @param int $nroots RS code generator polynomial degree (number of roots)
2676 * @param int $pad padding bytes at front of shortened block
2677 * @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>.
2678 */
2679 protected function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
2680 foreach ($this->rsitems as $rs) {
2681 if (($rs['pad'] != $pad) OR ($rs['nroots'] != $nroots) OR ($rs['mm'] != $symsize)
2682 OR ($rs['gfpoly'] != $gfpoly) OR ($rs['fcr'] != $fcr) OR ($rs['prim'] != $prim)) {
2683 continue;
2684 }
2685 return $rs;
2686 }
2687 $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
2688 array_unshift($this->rsitems, $rs);
2689 return $rs;
2690 }
2691
2692 // - - - - - - - - - - - - - - - - - - - - - - - - -
2693
2694 // QRrsItem
2695
2696 /**
2697 * modnn
2698 * @param array $rs RS values
2699 * @param int $x X position
2700 * @return int X osition
2701 */
2702 protected function modnn($rs, $x) {
2703 while ($x >= $rs['nn']) {
2704 $x -= $rs['nn'];
2705 $x = ($x >> $rs['mm']) + ($x & $rs['nn']);
2706 }
2707 return $x;
2708 }
2709
2710 /**
2711 * Initialize a Reed-Solomon codec and returns an array of values.
2712 * @param int $symsize symbol size, bits
2713 * @param int $gfpoly Field generator polynomial coefficients
2714 * @param int $fcr first root of RS code generator polynomial, index form
2715 * @param int $prim primitive element to generate polynomial roots
2716 * @param int $nroots RS code generator polynomial degree (number of roots)
2717 * @param int $pad padding bytes at front of shortened block
2718 * @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>.
2719 */
2720 protected function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
2721 // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2)
2722 $rs = null;
2723 // Check parameter ranges
2724 if (($symsize < 0) OR ($symsize > 8)) {
2725 return $rs;
2726 }
2727 if (($fcr < 0) OR ($fcr >= (1<<$symsize))) {
2728 return $rs;
2729 }
2730 if (($prim <= 0) OR ($prim >= (1<<$symsize))) {
2731 return $rs;
2732 }
2733 if (($nroots < 0) OR ($nroots >= (1<<$symsize))) {
2734 return $rs;
2735 }
2736 if (($pad < 0) OR ($pad >= ((1<<$symsize) -1 - $nroots))) {
2737 return $rs;
2738 }
2739 $rs = array();
2740 $rs['mm'] = $symsize;
2741 $rs['nn'] = (1 << $symsize) - 1;
2742 $rs['pad'] = $pad;
2743 $rs['alpha_to'] = array_fill(0, ($rs['nn'] + 1), 0);
2744 $rs['index_of'] = array_fill(0, ($rs['nn'] + 1), 0);
2745 // PHP style macro replacement ;)
2746 $NN =& $rs['nn'];
2747 $A0 =& $NN;
2748 // Generate Galois field lookup tables
2749 $rs['index_of'][0] = $A0; // log(zero) = -inf
2750 $rs['alpha_to'][$A0] = 0; // alpha**-inf = 0
2751 $sr = 1;
2752 for ($i=0; $i<$rs['nn']; ++$i) {
2753 $rs['index_of'][$sr] = $i;
2754 $rs['alpha_to'][$i] = $sr;
2755 $sr <<= 1;
2756 if ($sr & (1 << $symsize)) {
2757 $sr ^= $gfpoly;
2758 }
2759 $sr &= $rs['nn'];
2760 }
2761 if ($sr != 1) {
2762 // field generator polynomial is not primitive!
2763 return NULL;
2764 }
2765 // Form RS code generator polynomial from its roots
2766 $rs['genpoly'] = array_fill(0, ($nroots + 1), 0);
2767 $rs['fcr'] = $fcr;
2768 $rs['prim'] = $prim;
2769 $rs['nroots'] = $nroots;
2770 $rs['gfpoly'] = $gfpoly;
2771 // Find prim-th root of 1, used in decoding
2772 for ($iprim=1; ($iprim % $prim) != 0; $iprim += $rs['nn']) {
2773 ; // intentional empty-body loop!
2774 }
2775 $rs['iprim'] = (int)($iprim / $prim);
2776 $rs['genpoly'][0] = 1;
2777 for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
2778 $rs['genpoly'][$i+1] = 1;
2779 // Multiply rs->genpoly[] by @**(root + x)
2780 for ($j = $i; $j > 0; --$j) {
2781 if ($rs['genpoly'][$j] != 0) {
2782 $rs['genpoly'][$j] = $rs['genpoly'][$j-1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)];
2783 } else {
2784 $rs['genpoly'][$j] = $rs['genpoly'][$j-1];
2785 }
2786 }
2787 // rs->genpoly[0] can never be zero
2788 $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)];
2789 }
2790 // convert rs->genpoly[] to index form for quicker encoding
2791 for ($i = 0; $i <= $nroots; ++$i) {
2792 $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]];
2793 }
2794 return $rs;
2795 }
2796
2797 /**
2798 * Encode a Reed-Solomon codec and returns the parity array
2799 * @param array $rs RS values
2800 * @param array $data data
2801 * @param array $parity parity
2802 * @return parity array
2803 */
2804 protected function encode_rs_char($rs, $data, $parity) {
2805 $MM =& $rs['mm']; // bits per symbol
2806 $NN =& $rs['nn']; // the total number of symbols in a RS block
2807 $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
2808 $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
2809 $GENPOLY =& $rs['genpoly']; // an array of NROOTS+1 elements containing the generator polynomial in index form
2810 $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
2811 $FCR =& $rs['fcr']; // first consecutive root, index form
2812 $PRIM =& $rs['prim']; // primitive element, index form
2813 $IPRIM =& $rs['iprim']; // prim-th root of 1, index form
2814 $PAD =& $rs['pad']; // the number of pad symbols in a block
2815 $A0 =& $NN;
2816 $parity = array_fill(0, $NROOTS, 0);
2817 for ($i=0; $i < ($NN - $NROOTS - $PAD); $i++) {
2818 $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
2819 if ($feedback != $A0) {
2820 // feedback term is non-zero
2821 // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
2822 // always be for the polynomials constructed by init_rs()
2823 $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback);
2824 for ($j=1; $j < $NROOTS; ++$j) {
2825 $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[($NROOTS - $j)])];
2826 }
2827 }
2828 // Shift
2829 array_shift($parity);
2830 if ($feedback != $A0) {
2831 array_push($parity, $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]);
2832 } else {
2833 array_push($parity, 0);
2834 }
2835 }
2836 return $parity;
2837 }
2838
2839 } // end QRcode class
2840
2841 //============================================================+
2842 // END OF FILE
2843 //============================================================+
2844