include/barcodes/qrcode.php Quellcode

qrcode.php
gehe zur Dokumentation dieser Datei
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 
82 // definitions
83 if (!defined('QRCODEDEFS')) {
84 
88  define('QRCODEDEFS', true);
89 
90  // -----------------------------------------------------
91 
92  // Encoding modes (characters which can be encoded in QRcode)
93 
97  define('QR_MODE_NL', -1);
98 
102  define('QR_MODE_NM', 0);
103 
107  define('QR_MODE_AN', 1);
108 
112  define('QR_MODE_8B', 2);
113 
117  define('QR_MODE_KJ', 3);
118 
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 
133  define('QR_ECLEVEL_L', 0);
134 
138  define('QR_ECLEVEL_M', 1);
139 
143  define('QR_ECLEVEL_Q', 2);
144 
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 
160  define('QRSPEC_VERSION_MAX', 40);
161 
165  define('QRSPEC_WIDTH_MAX', 177);
166 
167  // -----------------------------------------------------
168 
172  define('QRCAP_WIDTH', 0);
173 
177  define('QRCAP_WORDS', 1);
178 
182  define('QRCAP_REMINDER', 2);
183 
187  define('QRCAP_EC', 3);
188 
189  // -----------------------------------------------------
190 
191  // Structure (currently usupported)
192 
196  define('STRUCTURE_HEADER_BITS', 20);
197 
201  define('MAX_STRUCTURED_SYMBOLS', 16);
202 
203  // -----------------------------------------------------
204 
205  // Masks
206 
210  define('N1', 3);
211 
215  define('N2', 3);
216 
220  define('N3', 40);
221 
225  define('N4', 10);
226 
227  // -----------------------------------------------------
228 
229  // Optimization settings
230 
234  define('QR_FIND_BEST_MASK', true);
235 
239  define('QR_FIND_FROM_RANDOM', 2);
240 
244  define('QR_DEFAULT_MASK', 2);
245 
246  // -----------------------------------------------------
247 
248 } // end of definitions
249 
250 // #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#
251 
252 // for compatibility with PHP4
253 if (!function_exists('str_split')) {
260  function str_split($string, $split_length=1) {
261  if ((strlen($string) > $split_length) OR (!$split_length)) {
262  do {
263  $c = strlen($string);
264  $parts[] = substr($string, 0, $split_length);
265  $string = substr($string, $split_length);
266  } while ($string !== false);
267  } else {
268  $parts = array($string);
269  }
270  return $parts;
271  }
272 }
273 
274 // #####################################################
275 
291 class QRcode {
292 
297  protected $barcode_array = array();
298 
303  protected $version = 0;
304 
309  protected $level = QR_ECLEVEL_L;
310 
315  protected $hint = QR_MODE_8B;
316 
321  protected $casesensitive = true;
322 
327  protected $structured = 0;
328 
333  protected $data;
334 
335  // FrameFiller
336 
341  protected $width;
342 
347  protected $frame;
348 
353  protected $x;
354 
359  protected $y;
360 
365  protected $dir;
366 
371  protected $bit;
372 
373  // ---- QRrawcode ----
374 
379  protected $datacode = array();
380 
385  protected $ecccode = array();
386 
391  protected $blocks;
392 
397  protected $rsblocks = array(); //of RSblock
398 
403  protected $count;
404 
409  protected $dataLength;
410 
415  protected $eccLength;
416 
421  protected $b1;
422 
423  // ---- QRmask ----
424 
429  protected $runLength = array();
430 
431  // ---- QRsplit ----
432 
437  protected $dataStr = '';
438 
443  protected $items;
444 
445  // Reed-Solomon items
446 
451  protected $rsitems = array();
452 
457  protected $frames = array();
458 
463  protected $anTable = array(
464  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
465  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
466  36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, //
467  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, //
468  -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, //
469  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, //
470  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
471  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 //
472  );
473 
479  protected $capacity = array(
480  array( 0, 0, 0, array( 0, 0, 0, 0)), //
481  array( 21, 26, 0, array( 7, 10, 13, 17)), // 1
482  array( 25, 44, 7, array( 10, 16, 22, 28)), //
483  array( 29, 70, 7, array( 15, 26, 36, 44)), //
484  array( 33, 100, 7, array( 20, 36, 52, 64)), //
485  array( 37, 134, 7, array( 26, 48, 72, 88)), // 5
486  array( 41, 172, 7, array( 36, 64, 96, 112)), //
487  array( 45, 196, 0, array( 40, 72, 108, 130)), //
488  array( 49, 242, 0, array( 48, 88, 132, 156)), //
489  array( 53, 292, 0, array( 60, 110, 160, 192)), //
490  array( 57, 346, 0, array( 72, 130, 192, 224)), // 10
491  array( 61, 404, 0, array( 80, 150, 224, 264)), //
492  array( 65, 466, 0, array( 96, 176, 260, 308)), //
493  array( 69, 532, 0, array( 104, 198, 288, 352)), //
494  array( 73, 581, 3, array( 120, 216, 320, 384)), //
495  array( 77, 655, 3, array( 132, 240, 360, 432)), // 15
496  array( 81, 733, 3, array( 144, 280, 408, 480)), //
497  array( 85, 815, 3, array( 168, 308, 448, 532)), //
498  array( 89, 901, 3, array( 180, 338, 504, 588)), //
499  array( 93, 991, 3, array( 196, 364, 546, 650)), //
500  array( 97, 1085, 3, array( 224, 416, 600, 700)), // 20
501  array(101, 1156, 4, array( 224, 442, 644, 750)), //
502  array(105, 1258, 4, array( 252, 476, 690, 816)), //
503  array(109, 1364, 4, array( 270, 504, 750, 900)), //
504  array(113, 1474, 4, array( 300, 560, 810, 960)), //
505  array(117, 1588, 4, array( 312, 588, 870, 1050)), // 25
506  array(121, 1706, 4, array( 336, 644, 952, 1110)), //
507  array(125, 1828, 4, array( 360, 700, 1020, 1200)), //
508  array(129, 1921, 3, array( 390, 728, 1050, 1260)), //
509  array(133, 2051, 3, array( 420, 784, 1140, 1350)), //
510  array(137, 2185, 3, array( 450, 812, 1200, 1440)), // 30
511  array(141, 2323, 3, array( 480, 868, 1290, 1530)), //
512  array(145, 2465, 3, array( 510, 924, 1350, 1620)), //
513  array(149, 2611, 3, array( 540, 980, 1440, 1710)), //
514  array(153, 2761, 3, array( 570, 1036, 1530, 1800)), //
515  array(157, 2876, 0, array( 570, 1064, 1590, 1890)), // 35
516  array(161, 3034, 0, array( 600, 1120, 1680, 1980)), //
517  array(165, 3196, 0, array( 630, 1204, 1770, 2100)), //
518  array(169, 3362, 0, array( 660, 1260, 1860, 2220)), //
519  array(173, 3532, 0, array( 720, 1316, 1950, 2310)), //
520  array(177, 3706, 0, array( 750, 1372, 2040, 2430)) // 40
521  );
522 
527  protected $lengthTableBits = array(
528  array(10, 12, 14),
529  array( 9, 11, 13),
530  array( 8, 16, 16),
531  array( 8, 10, 12)
532  );
533 
539  protected $eccTable = array(
540  array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)), //
541  array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1
542  array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), //
543  array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)), //
544  array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)), //
545  array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5
546  array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)), //
547  array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)), //
548  array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)), //
549  array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)), //
550  array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), // 10
551  array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)), //
552  array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)), //
553  array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)), //
554  array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)), //
555  array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), // 15
556  array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)), //
557  array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)), //
558  array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)), //
559  array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)), //
560  array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), // 20
561  array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)), //
562  array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)), //
563  array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)), //
564  array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)), //
565  array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), // 25
566  array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)), //
567  array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)), //
568  array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)), //
569  array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)), //
570  array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), // 30
571  array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)), //
572  array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)), //
573  array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)), //
574  array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)), //
575  array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), // 35
576  array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)), //
577  array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)), //
578  array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)), //
579  array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)), //
580  array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)) // 40
581  );
582 
589  protected $alignmentPattern = array(
590  array( 0, 0),
591  array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5
592  array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10
593  array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), // 11-15
594  array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), // 16-20
595  array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), // 21-25
596  array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), // 26-30
597  array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), // 31-35
598  array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58) // 35-40
599  );
600 
607  protected $versionPattern = array(
608  0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, //
609  0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, //
610  0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, //
611  0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, //
612  0x27541, 0x28c69
613  );
614 
619  protected $formatInfo = array(
620  array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976), //
621  array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0), //
622  array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed), //
623  array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b) //
624  );
625 
626 
627  // -------------------------------------------------
628  // -------------------------------------------------
629 
630 
639  public function __construct($code, $eclevel = 'L') {
640  $barcode_array = array();
641  if ((is_null($code)) OR ($code == '\0') OR ($code == '')) {
642  return false;
643  }
644  // set error correction level
645  $this->level = array_search($eclevel, array('L', 'M', 'Q', 'H'));
646  if ($this->level === false) {
647  $this->level = QR_ECLEVEL_L;
648  }
649  if (($this->hint != QR_MODE_8B) AND ($this->hint != QR_MODE_KJ)) {
650  return false;
651  }
652  if (($this->version < 0) OR ($this->version > QRSPEC_VERSION_MAX)) {
653  return false;
654  }
655  $this->items = array();
656  $this->encodeString($code);
657  if (is_null($this->data)) {
658  return false;
659  }
660  $qrTab = $this->binarize($this->data);
661  $size = count($qrTab);
662  $barcode_array['num_rows'] = $size;
663  $barcode_array['num_cols'] = $size;
664  $barcode_array['bcode'] = array();
665  foreach ($qrTab as $line) {
666  $arrAdd = array();
667  foreach (str_split($line) as $char) {
668  $arrAdd[] = ($char=='1')?1:0;
669  }
670  $barcode_array['bcode'][] = $arrAdd;
671  }
672  $this->barcode_array = $barcode_array;
673  }
674 
680  public function getBarcodeArray() {
681  return $this->barcode_array;
682  }
683 
689  protected function binarize($frame) {
690  $len = count($frame);
691  // the frame is square (width = height)
692  foreach ($frame as &$frameLine) {
693  for ($i=0; $i<$len; $i++) {
694  $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
695  }
696  }
697  return $frame;
698  }
699 
704  protected function encodeString($string) {
705  $this->dataStr = $string;
706  if (!$this->casesensitive) {
707  $this->toUpper();
708  }
709  $ret = $this->splitString();
710  if ($ret < 0) {
711  return NULL;
712  }
713  $this->encodeMask(-1);
714  }
715 
720  protected function encodeMask($mask) {
721  $spec = array(0, 0, 0, 0, 0);
722  $this->datacode = $this->getByteStream($this->items);
723  if (is_null($this->datacode)) {
724  return NULL;
725  }
726  $spec = $this->getEccSpec($this->version, $this->level, $spec);
727  $this->b1 = $this->rsBlockNum1($spec);
728  $this->dataLength = $this->rsDataLength($spec);
729  $this->eccLength = $this->rsEccLength($spec);
730  $this->ecccode = array_fill(0, $this->eccLength, 0);
731  $this->blocks = $this->rsBlockNum($spec);
732  $ret = $this->init($spec);
733  if ($ret < 0) {
734  return NULL;
735  }
736  $this->count = 0;
737  $this->width = $this->getWidth($this->version);
738  $this->frame = $this->newFrame($this->version);
739  $this->x = $this->width - 1;
740  $this->y = $this->width - 1;
741  $this->dir = -1;
742  $this->bit = -1;
743  // inteleaved data and ecc codes
744  for ($i=0; $i < ($this->dataLength + $this->eccLength); $i++) {
745  $code = $this->getCode();
746  $bit = 0x80;
747  for ($j=0; $j<8; $j++) {
748  $addr = $this->getNextPosition();
749  $this->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
750  $bit = $bit >> 1;
751  }
752  }
753  // remainder bits
754  $j = $this->getRemainder($this->version);
755  for ($i=0; $i<$j; $i++) {
756  $addr = $this->getNextPosition();
757  $this->setFrameAt($addr, 0x02);
758  }
759  // masking
760  $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
761  if ($mask < 0) {
762  if (QR_FIND_BEST_MASK) {
763  $masked = $this->mask($this->width, $this->frame, $this->level);
764  } else {
765  $masked = $this->makeMask($this->width, $this->frame, (intval(QR_DEFAULT_MASK) % 8), $this->level);
766  }
767  } else {
768  $masked = $this->makeMask($this->width, $this->frame, $mask, $this->level);
769  }
770  if ($masked == NULL) {
771  return NULL;
772  }
773  $this->data = $masked;
774  }
775 
776  // - - - - - - - - - - - - - - - - - - - - - - - - -
777 
778  // FrameFiller
779 
785  protected function setFrameAt($at, $val) {
786  $this->frame[$at['y']][$at['x']] = chr($val);
787  }
788 
794  protected function getFrameAt($at) {
795  return ord($this->frame[$at['y']][$at['x']]);
796  }
797 
802  protected function getNextPosition() {
803  do {
804  if ($this->bit == -1) {
805  $this->bit = 0;
806  return array('x'=>$this->x, 'y'=>$this->y);
807  }
808  $x = $this->x;
809  $y = $this->y;
810  $w = $this->width;
811  if ($this->bit == 0) {
812  $x--;
813  $this->bit++;
814  } else {
815  $x++;
816  $y += $this->dir;
817  $this->bit--;
818  }
819  if ($this->dir < 0) {
820  if ($y < 0) {
821  $y = 0;
822  $x -= 2;
823  $this->dir = 1;
824  if ($x == 6) {
825  $x--;
826  $y = 9;
827  }
828  }
829  } else {
830  if ($y == $w) {
831  $y = $w - 1;
832  $x -= 2;
833  $this->dir = -1;
834  if ($x == 6) {
835  $x--;
836  $y -= 8;
837  }
838  }
839  }
840  if (($x < 0) OR ($y < 0)) {
841  return NULL;
842  }
843  $this->x = $x;
844  $this->y = $y;
845  } while(ord($this->frame[$y][$x]) & 0x80);
846  return array('x'=>$x, 'y'=>$y);
847  }
848 
849  // - - - - - - - - - - - - - - - - - - - - - - - - -
850 
851  // QRrawcode
852 
858  protected function init($spec) {
859  $dl = $this->rsDataCodes1($spec);
860  $el = $this->rsEccCodes1($spec);
861  $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
862  $blockNo = 0;
863  $dataPos = 0;
864  $eccPos = 0;
865  $endfor = $this->rsBlockNum1($spec);
866  for ($i=0; $i < $endfor; ++$i) {
867  $ecc = array_slice($this->ecccode, $eccPos);
868  $this->rsblocks[$blockNo] = array();
869  $this->rsblocks[$blockNo]['dataLength'] = $dl;
870  $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos);
871  $this->rsblocks[$blockNo]['eccLength'] = $el;
872  $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
873  $this->rsblocks[$blockNo]['ecc'] = $ecc;
874  $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
875  $dataPos += $dl;
876  $eccPos += $el;
877  $blockNo++;
878  }
879  if ($this->rsBlockNum2($spec) == 0) {
880  return 0;
881  }
882  $dl = $this->rsDataCodes2($spec);
883  $el = $this->rsEccCodes2($spec);
884  $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
885  if ($rs == NULL) {
886  return -1;
887  }
888  $endfor = $this->rsBlockNum2($spec);
889  for ($i=0; $i < $endfor; ++$i) {
890  $ecc = array_slice($this->ecccode, $eccPos);
891  $this->rsblocks[$blockNo] = array();
892  $this->rsblocks[$blockNo]['dataLength'] = $dl;
893  $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos);
894  $this->rsblocks[$blockNo]['eccLength'] = $el;
895  $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
896  $this->rsblocks[$blockNo]['ecc'] = $ecc;
897  $this->ecccode = array_merge(array_slice($this->ecccode, 0, $eccPos), $ecc);
898  $dataPos += $dl;
899  $eccPos += $el;
900  $blockNo++;
901  }
902  return 0;
903  }
904 
909  protected function getCode() {
910  if ($this->count < $this->dataLength) {
911  $row = $this->count % $this->blocks;
912  $col = $this->count / $this->blocks;
913  if ($col >= $this->rsblocks[0]['dataLength']) {
914  $row += $this->b1;
915  }
916  $ret = $this->rsblocks[$row]['data'][$col];
917  } elseif ($this->count < $this->dataLength + $this->eccLength) {
918  $row = ($this->count - $this->dataLength) % $this->blocks;
919  $col = ($this->count - $this->dataLength) / $this->blocks;
920  $ret = $this->rsblocks[$row]['ecc'][$col];
921  } else {
922  return 0;
923  }
924  $this->count++;
925  return $ret;
926  }
927 
928  // - - - - - - - - - - - - - - - - - - - - - - - - -
929 
930  // QRmask
931 
940  protected function writeFormatInformation($width, &$frame, $mask, $level) {
941  $blacks = 0;
942  $format = $this->getFormatInfo($mask, $level);
943  for ($i=0; $i<8; ++$i) {
944  if ($format & 1) {
945  $blacks += 2;
946  $v = 0x85;
947  } else {
948  $v = 0x84;
949  }
950  $frame[8][$width - 1 - $i] = chr($v);
951  if ($i < 6) {
952  $frame[$i][8] = chr($v);
953  } else {
954  $frame[$i + 1][8] = chr($v);
955  }
956  $format = $format >> 1;
957  }
958  for ($i=0; $i<7; ++$i) {
959  if ($format & 1) {
960  $blacks += 2;
961  $v = 0x85;
962  } else {
963  $v = 0x84;
964  }
965  $frame[$width - 7 + $i][8] = chr($v);
966  if ($i == 0) {
967  $frame[8][7] = chr($v);
968  } else {
969  $frame[8][6 - $i] = chr($v);
970  }
971  $format = $format >> 1;
972  }
973  return $blacks;
974  }
975 
982  protected function mask0($x, $y) {
983  return ($x + $y) & 1;
984  }
985 
992  protected function mask1($x, $y) {
993  return ($y & 1);
994  }
995 
1002  protected function mask2($x, $y) {
1003  return ($x % 3);
1004  }
1005 
1012  protected function mask3($x, $y) {
1013  return ($x + $y) % 3;
1014  }
1015 
1022  protected function mask4($x, $y) {
1023  return (((int)($y / 2)) + ((int)($x / 3))) & 1;
1024  }
1025 
1032  protected function mask5($x, $y) {
1033  return (($x * $y) & 1) + ($x * $y) % 3;
1034  }
1035 
1042  protected function mask6($x, $y) {
1043  return ((($x * $y) & 1) + ($x * $y) % 3) & 1;
1044  }
1045 
1052  protected function mask7($x, $y) {
1053  return ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
1054  }
1055 
1063  protected function generateMaskNo($maskNo, $width, $frame) {
1064  $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
1065  for ($y=0; $y<$width; ++$y) {
1066  for ($x=0; $x<$width; ++$x) {
1067  if (ord($frame[$y][$x]) & 0x80) {
1068  $bitMask[$y][$x] = 0;
1069  } else {
1070  $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
1071  $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
1072  }
1073  }
1074  }
1075  return $bitMask;
1076  }
1077 
1087  protected function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly=false) {
1088  $b = 0;
1089  $bitMask = array();
1090  $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
1091  if ($maskGenOnly) {
1092  return;
1093  }
1094  $d = $s;
1095  for ($y=0; $y<$width; ++$y) {
1096  for ($x=0; $x<$width; ++$x) {
1097  if ($bitMask[$y][$x] == 1) {
1098  $d[$y][$x] = chr(ord($s[$y][$x]) ^ ((int)($bitMask[$y][$x])));
1099  }
1100  $b += (int)(ord($d[$y][$x]) & 1);
1101  }
1102  }
1103  return $b;
1104  }
1105 
1114  protected function makeMask($width, $frame, $maskNo, $level) {
1115  $masked = array_fill(0, $width, str_repeat("\0", $width));
1116  $this->makeMaskNo($maskNo, $width, $frame, $masked);
1117  $this->writeFormatInformation($width, $masked, $maskNo, $level);
1118  return $masked;
1119  }
1120 
1126  protected function calcN1N3($length) {
1127  $demerit = 0;
1128  for ($i=0; $i<$length; ++$i) {
1129  if ($this->runLength[$i] >= 5) {
1130  $demerit += (N1 + ($this->runLength[$i] - 5));
1131  }
1132  if ($i & 1) {
1133  if (($i >= 3) AND ($i < ($length-2)) AND ($this->runLength[$i] % 3 == 0)) {
1134  $fact = (int)($this->runLength[$i] / 3);
1135  if (($this->runLength[$i-2] == $fact)
1136  AND ($this->runLength[$i-1] == $fact)
1137  AND ($this->runLength[$i+1] == $fact)
1138  AND ($this->runLength[$i+2] == $fact)) {
1139  if (($this->runLength[$i-3] < 0) OR ($this->runLength[$i-3] >= (4 * $fact))) {
1140  $demerit += N3;
1141  } elseif ((($i+3) >= $length) OR ($this->runLength[$i+3] >= (4 * $fact))) {
1142  $demerit += N3;
1143  }
1144  }
1145  }
1146  }
1147  }
1148  return $demerit;
1149  }
1150 
1157  protected function evaluateSymbol($width, $frame) {
1158  $head = 0;
1159  $demerit = 0;
1160  for ($y=0; $y<$width; ++$y) {
1161  $head = 0;
1162  $this->runLength[0] = 1;
1163  $frameY = $frame[$y];
1164  if ($y > 0) {
1165  $frameYM = $frame[$y-1];
1166  }
1167  for ($x=0; $x<$width; ++$x) {
1168  if (($x > 0) AND ($y > 0)) {
1169  $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
1170  $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
1171  if (($b22 | ($w22 ^ 1)) & 1) {
1172  $demerit += N2;
1173  }
1174  }
1175  if (($x == 0) AND (ord($frameY[$x]) & 1)) {
1176  $this->runLength[0] = -1;
1177  $head = 1;
1178  $this->runLength[$head] = 1;
1179  } elseif ($x > 0) {
1180  if ((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
1181  $head++;
1182  $this->runLength[$head] = 1;
1183  } else {
1184  $this->runLength[$head]++;
1185  }
1186  }
1187  }
1188  $demerit += $this->calcN1N3($head+1);
1189  }
1190  for ($x=0; $x<$width; ++$x) {
1191  $head = 0;
1192  $this->runLength[0] = 1;
1193  for ($y=0; $y<$width; ++$y) {
1194  if (($y == 0) AND (ord($frame[$y][$x]) & 1)) {
1195  $this->runLength[0] = -1;
1196  $head = 1;
1197  $this->runLength[$head] = 1;
1198  } elseif ($y > 0) {
1199  if ((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
1200  $head++;
1201  $this->runLength[$head] = 1;
1202  } else {
1203  $this->runLength[$head]++;
1204  }
1205  }
1206  }
1207  $demerit += $this->calcN1N3($head+1);
1208  }
1209  return $demerit;
1210  }
1211 
1219  protected function mask($width, $frame, $level) {
1220  $minDemerit = PHP_INT_MAX;
1221  $bestMaskNum = 0;
1222  $bestMask = array();
1223  $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7);
1224  if (QR_FIND_FROM_RANDOM !== false) {
1225  $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9);
1226  for ($i = 0; $i < $howManuOut; ++$i) {
1227  $remPos = rand (0, count($checked_masks)-1);
1228  unset($checked_masks[$remPos]);
1229  $checked_masks = array_values($checked_masks);
1230  }
1231  }
1232  $bestMask = $frame;
1233  foreach ($checked_masks as $i) {
1234  $mask = array_fill(0, $width, str_repeat("\0", $width));
1235  $demerit = 0;
1236  $blacks = 0;
1237  $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
1238  $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
1239  $blacks = (int)(100 * $blacks / ($width * $width));
1240  $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
1241  $demerit += $this->evaluateSymbol($width, $mask);
1242  if ($demerit < $minDemerit) {
1243  $minDemerit = $demerit;
1244  $bestMask = $mask;
1245  $bestMaskNum = $i;
1246  }
1247  }
1248  return $bestMask;
1249  }
1250 
1251  // - - - - - - - - - - - - - - - - - - - - - - - - -
1252 
1253  // QRsplit
1254 
1261  protected function isdigitat($str, $pos) {
1262  if ($pos >= strlen($str)) {
1263  return false;
1264  }
1265  return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
1266  }
1267 
1274  protected function isalnumat($str, $pos) {
1275  if ($pos >= strlen($str)) {
1276  return false;
1277  }
1278  return ($this->lookAnTable(ord($str[$pos])) >= 0);
1279  }
1280 
1286  protected function identifyMode($pos) {
1287  if ($pos >= strlen($this->dataStr)) {
1288  return QR_MODE_NL;
1289  }
1290  $c = $this->dataStr[$pos];
1291  if ($this->isdigitat($this->dataStr, $pos)) {
1292  return QR_MODE_NM;
1293  } elseif ($this->isalnumat($this->dataStr, $pos)) {
1294  return QR_MODE_AN;
1295  } elseif ($this->hint == QR_MODE_KJ) {
1296  if ($pos+1 < strlen($this->dataStr)) {
1297  $d = $this->dataStr[$pos+1];
1298  $word = (ord($c) << 8) | ord($d);
1299  if (($word >= 0x8140 && $word <= 0x9ffc) OR ($word >= 0xe040 && $word <= 0xebbf)) {
1300  return QR_MODE_KJ;
1301  }
1302  }
1303  }
1304  return QR_MODE_8B;
1305  }
1306 
1311  protected function eatNum() {
1312  $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1313  $p = 0;
1314  while($this->isdigitat($this->dataStr, $p)) {
1315  $p++;
1316  }
1317  $run = $p;
1318  $mode = $this->identifyMode($p);
1319  if ($mode == QR_MODE_8B) {
1320  $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1321  + $this->estimateBitsMode8(1) // + 4 + l8
1322  - $this->estimateBitsMode8($run + 1); // - 4 - l8
1323  if ($dif > 0) {
1324  return $this->eat8();
1325  }
1326  }
1327  if ($mode == QR_MODE_AN) {
1328  $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1329  + $this->estimateBitsModeAn(1) // + 4 + la
1330  - $this->estimateBitsModeAn($run + 1);// - 4 - la
1331  if ($dif > 0) {
1332  return $this->eatAn();
1333  }
1334  }
1335  $this->items = $this->appendNewInputItem($this->items, QR_MODE_NM, $run, str_split($this->dataStr));
1336  return $run;
1337  }
1338 
1343  protected function eatAn() {
1344  $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
1345  $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1346  $p =1 ;
1347  while($this->isalnumat($this->dataStr, $p)) {
1348  if ($this->isdigitat($this->dataStr, $p)) {
1349  $q = $p;
1350  while($this->isdigitat($this->dataStr, $q)) {
1351  $q++;
1352  }
1353  $dif = $this->estimateBitsModeAn($p) // + 4 + la
1354  + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1355  - $this->estimateBitsModeAn($q); // - 4 - la
1356  if ($dif < 0) {
1357  break;
1358  } else {
1359  $p = $q;
1360  }
1361  } else {
1362  $p++;
1363  }
1364  }
1365  $run = $p;
1366  if (!$this->isalnumat($this->dataStr, $p)) {
1367  $dif = $this->estimateBitsModeAn($run) + 4 + $la
1368  + $this->estimateBitsMode8(1) // + 4 + l8
1369  - $this->estimateBitsMode8($run + 1); // - 4 - l8
1370  if ($dif > 0) {
1371  return $this->eat8();
1372  }
1373  }
1374  $this->items = $this->appendNewInputItem($this->items, QR_MODE_AN, $run, str_split($this->dataStr));
1375  return $run;
1376  }
1377 
1382  protected function eatKanji() {
1383  $p = 0;
1384  while($this->identifyMode($p) == QR_MODE_KJ) {
1385  $p += 2;
1386  }
1387  $this->items = $this->appendNewInputItem($this->items, QR_MODE_KJ, $p, str_split($this->dataStr));
1388  return $run;
1389  }
1390 
1395  protected function eat8() {
1396  $la = $this->lengthIndicator(QR_MODE_AN, $this->version);
1397  $ln = $this->lengthIndicator(QR_MODE_NM, $this->version);
1398  $p = 1;
1399  $dataStrLen = strlen($this->dataStr);
1400  while($p < $dataStrLen) {
1401  $mode = $this->identifyMode($p);
1402  if ($mode == QR_MODE_KJ) {
1403  break;
1404  }
1405  if ($mode == QR_MODE_NM) {
1406  $q = $p;
1407  while($this->isdigitat($this->dataStr, $q)) {
1408  $q++;
1409  }
1410  $dif = $this->estimateBitsMode8($p) // + 4 + l8
1411  + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1412  - $this->estimateBitsMode8($q); // - 4 - l8
1413  if ($dif < 0) {
1414  break;
1415  } else {
1416  $p = $q;
1417  }
1418  } elseif ($mode == QR_MODE_AN) {
1419  $q = $p;
1420  while($this->isalnumat($this->dataStr, $q)) {
1421  $q++;
1422  }
1423  $dif = $this->estimateBitsMode8($p) // + 4 + l8
1424  + $this->estimateBitsModeAn($q - $p) + 4 + $la
1425  - $this->estimateBitsMode8($q); // - 4 - l8
1426  if ($dif < 0) {
1427  break;
1428  } else {
1429  $p = $q;
1430  }
1431  } else {
1432  $p++;
1433  }
1434  }
1435  $run = $p;
1436  $this->items = $this->appendNewInputItem($this->items, QR_MODE_8B, $run, str_split($this->dataStr));
1437  return $run;
1438  }
1439 
1444  protected function splitString() {
1445  while (strlen($this->dataStr) > 0) {
1446  $mode = $this->identifyMode(0);
1447  switch ($mode) {
1448  case QR_MODE_NM: {
1449  $length = $this->eatNum();
1450  break;
1451  }
1452  case QR_MODE_AN: {
1453  $length = $this->eatAn();
1454  break;
1455  }
1456  case QR_MODE_KJ: {
1457  if ($hint == QR_MODE_KJ) {
1458  $length = $this->eatKanji();
1459  } else {
1460  $length = $this->eat8();
1461  }
1462  break;
1463  }
1464  default: {
1465  $length = $this->eat8();
1466  break;
1467  }
1468  }
1469  if ($length == 0) {
1470  return 0;
1471  }
1472  if ($length < 0) {
1473  return -1;
1474  }
1475  $this->dataStr = substr($this->dataStr, $length);
1476  }
1477  return 0;
1478  }
1479 
1483  protected function toUpper() {
1484  $stringLen = strlen($this->dataStr);
1485  $p = 0;
1486  while ($p < $stringLen) {
1487  $mode = $this->identifyMode(substr($this->dataStr, $p), $this->hint);
1488  if ($mode == QR_MODE_KJ) {
1489  $p += 2;
1490  } else {
1491  if ((ord($this->dataStr[$p]) >= ord('a')) AND (ord($this->dataStr[$p]) <= ord('z'))) {
1492  $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
1493  }
1494  $p++;
1495  }
1496  }
1497  return $this->dataStr;
1498  }
1499 
1500  // - - - - - - - - - - - - - - - - - - - - - - - - -
1501 
1502  // QRinputItem
1503 
1512  protected function newInputItem($mode, $size, $data, $bstream=null) {
1513  $setData = array_slice($data, 0, $size);
1514  if (count($setData) < $size) {
1515  $setData = array_merge($setData, array_fill(0, ($size - count($setData)), 0));
1516  }
1517  if (!$this->check($mode, $size, $setData)) {
1518  return NULL;
1519  }
1520  $inputitem = array();
1521  $inputitem['mode'] = $mode;
1522  $inputitem['size'] = $size;
1523  $inputitem['data'] = $setData;
1524  $inputitem['bstream'] = $bstream;
1525  return $inputitem;
1526  }
1527 
1534  protected function encodeModeNum($inputitem, $version) {
1535  $words = (int)($inputitem['size'] / 3);
1536  $inputitem['bstream'] = array();
1537  $val = 0x1;
1538  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1539  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_NM, $version), $inputitem['size']);
1540  for ($i=0; $i < $words; ++$i) {
1541  $val = (ord($inputitem['data'][$i*3 ]) - ord('0')) * 100;
1542  $val += (ord($inputitem['data'][$i*3+1]) - ord('0')) * 10;
1543  $val += (ord($inputitem['data'][$i*3+2]) - ord('0'));
1544  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
1545  }
1546  if ($inputitem['size'] - $words * 3 == 1) {
1547  $val = ord($inputitem['data'][$words*3]) - ord('0');
1548  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1549  } elseif (($inputitem['size'] - ($words * 3)) == 2) {
1550  $val = (ord($inputitem['data'][$words*3 ]) - ord('0')) * 10;
1551  $val += (ord($inputitem['data'][$words*3+1]) - ord('0'));
1552  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
1553  }
1554  return $inputitem;
1555  }
1556 
1563  protected function encodeModeAn($inputitem, $version) {
1564  $words = (int)($inputitem['size'] / 2);
1565  $inputitem['bstream'] = array();
1566  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02);
1567  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_AN, $version), $inputitem['size']);
1568  for ($i=0; $i < $words; ++$i) {
1569  $val = (int)($this->lookAnTable(ord($inputitem['data'][$i*2])) * 45);
1570  $val += (int)($this->lookAnTable(ord($inputitem['data'][($i*2)+1])));
1571  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
1572  }
1573  if ($inputitem['size'] & 1) {
1574  $val = $this->lookAnTable(ord($inputitem['data'][($words * 2)]));
1575  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
1576  }
1577  return $inputitem;
1578  }
1579 
1586  protected function encodeMode8($inputitem, $version) {
1587  $inputitem['bstream'] = array();
1588  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
1589  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_8B, $version), $inputitem['size']);
1590  for ($i=0; $i < $inputitem['size']; ++$i) {
1591  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][$i]));
1592  }
1593  return $inputitem;
1594  }
1595 
1602  protected function encodeModeKanji($inputitem, $version) {
1603  $inputitem['bstream'] = array();
1604  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
1605  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_KJ, $version), (int)($inputitem['size'] / 2));
1606  for ($i=0; $i<$inputitem['size']; $i+=2) {
1607  $val = (ord($inputitem['data'][$i]) << 8) | ord($inputitem['data'][$i+1]);
1608  if ($val <= 0x9ffc) {
1609  $val -= 0x8140;
1610  } else {
1611  $val -= 0xc140;
1612  }
1613  $h = ($val >> 8) * 0xc0;
1614  $val = ($val & 0xff) + $h;
1615  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
1616  }
1617  return $inputitem;
1618  }
1619 
1625  protected function encodeModeStructure($inputitem) {
1626  $inputitem['bstream'] = array();
1627  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03);
1628  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][1]) - 1);
1629  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][0]) - 1);
1630  $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][2]));
1631  return $inputitem;
1632  }
1633 
1640  protected function encodeBitStream($inputitem, $version) {
1641  $inputitem['bstream'] = array();
1642  $words = $this->maximumWords($inputitem['mode'], $version);
1643  if ($inputitem['size'] > $words) {
1644  $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
1645  $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, array_slice($inputitem['data'], $words));
1646  $st1 = $this->encodeBitStream($st1, $version);
1647  $st2 = $this->encodeBitStream($st2, $version);
1648  $inputitem['bstream'] = array();
1649  $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
1650  $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
1651  } else {
1652  switch($inputitem['mode']) {
1653  case QR_MODE_NM: {
1654  $inputitem = $this->encodeModeNum($inputitem, $version);
1655  break;
1656  }
1657  case QR_MODE_AN: {
1658  $inputitem = $this->encodeModeAn($inputitem, $version);
1659  break;
1660  }
1661  case QR_MODE_8B: {
1662  $inputitem = $this->encodeMode8($inputitem, $version);
1663  break;
1664  }
1665  case QR_MODE_KJ: {
1666  $inputitem = $this->encodeModeKanji($inputitem, $version);
1667  break;
1668  }
1669  case QR_MODE_ST: {
1670  $inputitem = $this->encodeModeStructure($inputitem);
1671  break;
1672  }
1673  default: {
1674  break;
1675  }
1676  }
1677  }
1678  return $inputitem;
1679  }
1680 
1681  // - - - - - - - - - - - - - - - - - - - - - - - - -
1682 
1683  // QRinput
1684 
1695  protected function appendNewInputItem($items, $mode, $size, $data) {
1696  $newitem = $this->newInputItem($mode, $size, $data);
1697  if (!empty($newitem)) {
1698  $items[] = $newitem;
1699  }
1700  return $items;
1701  }
1702 
1711  protected function insertStructuredAppendHeader($items, $size, $index, $parity) {
1712  if ($size > MAX_STRUCTURED_SYMBOLS) {
1713  return -1;
1714  }
1715  if (($index <= 0) OR ($index > MAX_STRUCTURED_SYMBOLS)) {
1716  return -1;
1717  }
1718  $buf = array($size, $index, $parity);
1719  $entry = $this->newInputItem(QR_MODE_ST, 3, buf);
1720  array_unshift($items, $entry);
1721  return $items;
1722  }
1723 
1729  protected function calcParity($items) {
1730  $parity = 0;
1731  foreach ($items as $item) {
1732  if ($item['mode'] != QR_MODE_ST) {
1733  for ($i=$item['size']-1; $i>=0; --$i) {
1734  $parity ^= $item['data'][$i];
1735  }
1736  }
1737  }
1738  return $parity;
1739  }
1740 
1747  protected function checkModeNum($size, $data) {
1748  for ($i=0; $i<$size; ++$i) {
1749  if ((ord($data[$i]) < ord('0')) OR (ord($data[$i]) > ord('9'))){
1750  return false;
1751  }
1752  }
1753  return true;
1754  }
1755 
1761  protected function lookAnTable($c) {
1762  return (($c > 127)?-1:$this->anTable[$c]);
1763  }
1764 
1771  protected function checkModeAn($size, $data) {
1772  for ($i=0; $i<$size; ++$i) {
1773  if ($this->lookAnTable(ord($data[$i])) == -1) {
1774  return false;
1775  }
1776  }
1777  return true;
1778  }
1779 
1785  protected function estimateBitsModeNum($size) {
1786  $w = (int)($size / 3);
1787  $bits = ($w * 10);
1788  switch($size - ($w * 3)) {
1789  case 1: {
1790  $bits += 4;
1791  break;
1792  }
1793  case 2: {
1794  $bits += 7;
1795  break;
1796  }
1797  }
1798  return $bits;
1799  }
1800 
1806  protected function estimateBitsModeAn($size) {
1807  $bits = (int)($size * 5.5); // (size / 2 ) * 11
1808  if ($size & 1) {
1809  $bits += 6;
1810  }
1811  return $bits;
1812  }
1813 
1819  protected function estimateBitsMode8($size) {
1820  return (int)($size * 8);
1821  }
1822 
1828  protected function estimateBitsModeKanji($size) {
1829  return (int)($size * 6.5); // (size / 2 ) * 13
1830  }
1831 
1838  protected function checkModeKanji($size, $data) {
1839  if ($size & 1) {
1840  return false;
1841  }
1842  for ($i=0; $i<$size; $i+=2) {
1843  $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
1844  if (($val < 0x8140) OR (($val > 0x9ffc) AND ($val < 0xe040)) OR ($val > 0xebbf)) {
1845  return false;
1846  }
1847  }
1848  return true;
1849  }
1850 
1858  protected function check($mode, $size, $data) {
1859  if ($size <= 0) {
1860  return false;
1861  }
1862  switch($mode) {
1863  case QR_MODE_NM: {
1864  return $this->checkModeNum($size, $data);
1865  }
1866  case QR_MODE_AN: {
1867  return $this->checkModeAn($size, $data);
1868  }
1869  case QR_MODE_KJ: {
1870  return $this->checkModeKanji($size, $data);
1871  }
1872  case QR_MODE_8B: {
1873  return true;
1874  }
1875  case QR_MODE_ST: {
1876  return true;
1877  }
1878  default: {
1879  break;
1880  }
1881  }
1882  return false;
1883  }
1884 
1891  protected function estimateBitStreamSize($items, $version) {
1892  $bits = 0;
1893  if ($version == 0) {
1894  $version = 1;
1895  }
1896  foreach ($items as $item) {
1897  switch($item['mode']) {
1898  case QR_MODE_NM: {
1899  $bits = $this->estimateBitsModeNum($item['size']);
1900  break;
1901  }
1902  case QR_MODE_AN: {
1903  $bits = $this->estimateBitsModeAn($item['size']);
1904  break;
1905  }
1906  case QR_MODE_8B: {
1907  $bits = $this->estimateBitsMode8($item['size']);
1908  break;
1909  }
1910  case QR_MODE_KJ: {
1911  $bits = $this->estimateBitsModeKanji($item['size']);
1912  break;
1913  }
1914  case QR_MODE_ST: {
1915  return STRUCTURE_HEADER_BITS;
1916  }
1917  default: {
1918  return 0;
1919  }
1920  }
1921  $l = $this->lengthIndicator($item['mode'], $version);
1922  $m = 1 << $l;
1923  $num = (int)(($item['size'] + $m - 1) / $m);
1924  $bits += $num * (4 + $l);
1925  }
1926  return $bits;
1927  }
1928 
1934  protected function estimateVersion($items) {
1935  $version = 0;
1936  $prev = 0;
1937  do {
1938  $prev = $version;
1939  $bits = $this->estimateBitStreamSize($items, $prev);
1940  $version = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
1941  if ($version < 0) {
1942  return -1;
1943  }
1944  } while ($version > $prev);
1945  return $version;
1946  }
1947 
1955  protected function lengthOfCode($mode, $version, $bits) {
1956  $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
1957  switch($mode) {
1958  case QR_MODE_NM: {
1959  $chunks = (int)($payload / 10);
1960  $remain = $payload - $chunks * 10;
1961  $size = $chunks * 3;
1962  if ($remain >= 7) {
1963  $size += 2;
1964  } elseif ($remain >= 4) {
1965  $size += 1;
1966  }
1967  break;
1968  }
1969  case QR_MODE_AN: {
1970  $chunks = (int)($payload / 11);
1971  $remain = $payload - $chunks * 11;
1972  $size = $chunks * 2;
1973  if ($remain >= 6) {
1974  ++$size;
1975  }
1976  break;
1977  }
1978  case QR_MODE_8B: {
1979  $size = (int)($payload / 8);
1980  break;
1981  }
1982  case QR_MODE_KJ: {
1983  $size = (int)(($payload / 13) * 2);
1984  break;
1985  }
1986  case QR_MODE_ST: {
1987  $size = (int)($payload / 8);
1988  break;
1989  }
1990  default: {
1991  $size = 0;
1992  break;
1993  }
1994  }
1995  $maxsize = $this->maximumWords($mode, $version);
1996  if ($size < 0) {
1997  $size = 0;
1998  }
1999  if ($size > $maxsize) {
2000  $size = $maxsize;
2001  }
2002  return $size;
2003  }
2004 
2010  protected function createBitStream($items) {
2011  $total = 0;
2012  foreach ($items as $key => $item) {
2013  $items[$key] = $this->encodeBitStream($item, $this->version);
2014  $bits = count($items[$key]['bstream']);
2015  $total += $bits;
2016  }
2017  return array($items, $total);
2018  }
2019 
2025  protected function convertData($items) {
2026  $ver = $this->estimateVersion($items);
2027  if ($ver > $this->version) {
2028  $this->version = $ver;
2029  }
2030  while (true) {
2031  $cbs = $this->createBitStream($items);
2032  $items = $cbs[0];
2033  $bits = $cbs[1];
2034  if ($bits < 0) {
2035  return -1;
2036  }
2037  $ver = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level);
2038  if ($ver < 0) {
2039  return -1;
2040  } elseif ($ver > $this->version) {
2041  $this->version = $ver;
2042  } else {
2043  break;
2044  }
2045  }
2046  return $items;
2047  }
2048 
2054  protected function appendPaddingBit($bstream) {
2055  if (is_null($bstream)) {
2056  return null;
2057  }
2058  $bits = count($bstream);
2059  $maxwords = $this->getDataLength($this->version, $this->level);
2060  $maxbits = $maxwords * 8;
2061  if ($maxbits == $bits) {
2062  return $bstream;
2063  }
2064  if ($maxbits - $bits < 5) {
2065  return $this->appendNum($bstream, $maxbits - $bits, 0);
2066  }
2067  $bits += 4;
2068  $words = (int)(($bits + 7) / 8);
2069  $padding = array();
2070  $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
2071  $padlen = $maxwords - $words;
2072  if ($padlen > 0) {
2073  $padbuf = array();
2074  for ($i=0; $i<$padlen; ++$i) {
2075  $padbuf[$i] = ($i&1)?0x11:0xec;
2076  }
2077  $padding = $this->appendBytes($padding, $padlen, $padbuf);
2078  }
2079  return $this->appendBitstream($bstream, $padding);
2080  }
2081 
2087  protected function mergeBitStream($items) {
2088  $items = $this->convertData($items);
2089  if (!is_array($items)) {
2090  return null;
2091  }
2092  $bstream = array();
2093  foreach ($items as $item) {
2094  $bstream = $this->appendBitstream($bstream, $item['bstream']);
2095  }
2096  return $bstream;
2097  }
2098 
2104  protected function getBitStream($items) {
2105  $bstream = $this->mergeBitStream($items);
2106  return $this->appendPaddingBit($bstream);
2107  }
2108 
2114  protected function getByteStream($items) {
2115  $bstream = $this->getBitStream($items);
2116  return $this->bitstreamToByte($bstream);
2117  }
2118 
2119  // - - - - - - - - - - - - - - - - - - - - - - - - -
2120 
2121  // QRbitstream
2122 
2128  protected function allocate($setLength) {
2129  return array_fill(0, $setLength, 0);
2130  }
2131 
2138  protected function newFromNum($bits, $num) {
2139  $bstream = $this->allocate($bits);
2140  $mask = 1 << ($bits - 1);
2141  for ($i=0; $i<$bits; ++$i) {
2142  if ($num & $mask) {
2143  $bstream[$i] = 1;
2144  } else {
2145  $bstream[$i] = 0;
2146  }
2147  $mask = $mask >> 1;
2148  }
2149  return $bstream;
2150  }
2151 
2158  protected function newFromBytes($size, $data) {
2159  $bstream = $this->allocate($size * 8);
2160  $p=0;
2161  for ($i=0; $i<$size; ++$i) {
2162  $mask = 0x80;
2163  for ($j=0; $j<8; ++$j) {
2164  if ($data[$i] & $mask) {
2165  $bstream[$p] = 1;
2166  } else {
2167  $bstream[$p] = 0;
2168  }
2169  $p++;
2170  $mask = $mask >> 1;
2171  }
2172  }
2173  return $bstream;
2174  }
2175 
2182  protected function appendBitstream($bitstream, $append) {
2183  if ((!is_array($append)) OR (count($append) == 0)) {
2184  return $bitstream;
2185  }
2186  if (count($bitstream) == 0) {
2187  return $append;
2188  }
2189  return array_values(array_merge($bitstream, $append));
2190  }
2191 
2199  protected function appendNum($bitstream, $bits, $num) {
2200  if ($bits == 0) {
2201  return 0;
2202  }
2203  $b = $this->newFromNum($bits, $num);
2204  return $this->appendBitstream($bitstream, $b);
2205  }
2206 
2214  protected function appendBytes($bitstream, $size, $data) {
2215  if ($size == 0) {
2216  return 0;
2217  }
2218  $b = $this->newFromBytes($size, $data);
2219  return $this->appendBitstream($bitstream, $b);
2220  }
2221 
2227  protected function bitstreamToByte($bstream) {
2228  if (is_null($bstream)) {
2229  return null;
2230  }
2231  $size = count($bstream);
2232  if ($size == 0) {
2233  return array();
2234  }
2235  $data = array_fill(0, (int)(($size + 7) / 8), 0);
2236  $bytes = (int)($size / 8);
2237  $p = 0;
2238  for ($i=0; $i<$bytes; $i++) {
2239  $v = 0;
2240  for ($j=0; $j<8; $j++) {
2241  $v = $v << 1;
2242  $v |= $bstream[$p];
2243  $p++;
2244  }
2245  $data[$i] = $v;
2246  }
2247  if ($size & 7) {
2248  $v = 0;
2249  for ($j=0; $j<($size & 7); $j++) {
2250  $v = $v << 1;
2251  $v |= $bstream[$p];
2252  $p++;
2253  }
2254  $data[$bytes] = $v;
2255  }
2256  return $data;
2257  }
2258 
2259  // - - - - - - - - - - - - - - - - - - - - - - - - -
2260 
2261  // QRspec
2262 
2272  protected function qrstrset($srctab, $x, $y, $repl, $replLen=false) {
2273  $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
2274  return $srctab;
2275  }
2276 
2283  protected function getDataLength($version, $level) {
2284  return $this->capacity[$version][QRCAP_WORDS] - $this->capacity[$version][QRCAP_EC][$level];
2285  }
2286 
2293  protected function getECCLength($version, $level){
2294  return $this->capacity[$version][QRCAP_EC][$level];
2295  }
2296 
2302  protected function getWidth($version) {
2303  return $this->capacity[$version][QRCAP_WIDTH];
2304  }
2305 
2311  protected function getRemainder($version) {
2312  return $this->capacity[$version][QRCAP_REMINDER];
2313  }
2314 
2321  protected function getMinimumVersion($size, $level) {
2322  for ($i = 1; $i <= QRSPEC_VERSION_MAX; ++$i) {
2323  $words = ($this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]);
2324  if ($words >= $size) {
2325  return $i;
2326  }
2327  }
2328  // the size of input data is greater than QR capacity, try to lover the error correction mode
2329  return -1;
2330  }
2331 
2338  protected function lengthIndicator($mode, $version) {
2339  if ($mode == QR_MODE_ST) {
2340  return 0;
2341  }
2342  if ($version <= 9) {
2343  $l = 0;
2344  } elseif ($version <= 26) {
2345  $l = 1;
2346  } else {
2347  $l = 2;
2348  }
2349  return $this->lengthTableBits[$mode][$l];
2350  }
2351 
2358  protected function maximumWords($mode, $version) {
2359  if ($mode == QR_MODE_ST) {
2360  return 3;
2361  }
2362  if ($version <= 9) {
2363  $l = 0;
2364  } else if ($version <= 26) {
2365  $l = 1;
2366  } else {
2367  $l = 2;
2368  }
2369  $bits = $this->lengthTableBits[$mode][$l];
2370  $words = (1 << $bits) - 1;
2371  if ($mode == QR_MODE_KJ) {
2372  $words *= 2; // the number of bytes is required
2373  }
2374  return $words;
2375  }
2376 
2384  protected function getEccSpec($version, $level, $spec) {
2385  if (count($spec) < 5) {
2386  $spec = array(0, 0, 0, 0, 0);
2387  }
2388  $b1 = $this->eccTable[$version][$level][0];
2389  $b2 = $this->eccTable[$version][$level][1];
2390  $data = $this->getDataLength($version, $level);
2391  $ecc = $this->getECCLength($version, $level);
2392  if ($b2 == 0) {
2393  $spec[0] = $b1;
2394  $spec[1] = (int)($data / $b1);
2395  $spec[2] = (int)($ecc / $b1);
2396  $spec[3] = 0;
2397  $spec[4] = 0;
2398  } else {
2399  $spec[0] = $b1;
2400  $spec[1] = (int)($data / ($b1 + $b2));
2401  $spec[2] = (int)($ecc / ($b1 + $b2));
2402  $spec[3] = $b2;
2403  $spec[4] = $spec[1] + 1;
2404  }
2405  return $spec;
2406  }
2407 
2415  protected function putAlignmentMarker($frame, $ox, $oy) {
2416  $finder = array(
2417  "\xa1\xa1\xa1\xa1\xa1",
2418  "\xa1\xa0\xa0\xa0\xa1",
2419  "\xa1\xa0\xa1\xa0\xa1",
2420  "\xa1\xa0\xa0\xa0\xa1",
2421  "\xa1\xa1\xa1\xa1\xa1"
2422  );
2423  $yStart = $oy - 2;
2424  $xStart = $ox - 2;
2425  for ($y=0; $y < 5; $y++) {
2426  $frame = $this->qrstrset($frame, $xStart, $yStart+$y, $finder[$y]);
2427  }
2428  return $frame;
2429  }
2430 
2438  protected function putAlignmentPattern($version, $frame, $width) {
2439  if ($version < 2) {
2440  return $frame;
2441  }
2442  $d = $this->alignmentPattern[$version][1] - $this->alignmentPattern[$version][0];
2443  if ($d < 0) {
2444  $w = 2;
2445  } else {
2446  $w = (int)(($width - $this->alignmentPattern[$version][0]) / $d + 2);
2447  }
2448  if ($w * $w - 3 == 1) {
2449  $x = $this->alignmentPattern[$version][0];
2450  $y = $this->alignmentPattern[$version][0];
2451  $frame = $this->putAlignmentMarker($frame, $x, $y);
2452  return $frame;
2453  }
2454  $cx = $this->alignmentPattern[$version][0];
2455  $wo = $w - 1;
2456  for ($x=1; $x < $wo; ++$x) {
2457  $frame = $this->putAlignmentMarker($frame, 6, $cx);
2458  $frame = $this->putAlignmentMarker($frame, $cx, 6);
2459  $cx += $d;
2460  }
2461  $cy = $this->alignmentPattern[$version][0];
2462  for ($y=0; $y < $wo; ++$y) {
2463  $cx = $this->alignmentPattern[$version][0];
2464  for ($x=0; $x < $wo; ++$x) {
2465  $frame = $this->putAlignmentMarker($frame, $cx, $cy);
2466  $cx += $d;
2467  }
2468  $cy += $d;
2469  }
2470  return $frame;
2471  }
2472 
2478  protected function getVersionPattern($version) {
2479  if (($version < 7) OR ($version > QRSPEC_VERSION_MAX)) {
2480  return 0;
2481  }
2482  return $this->versionPattern[($version - 7)];
2483  }
2484 
2491  protected function getFormatInfo($mask, $level) {
2492  if (($mask < 0) OR ($mask > 7)) {
2493  return 0;
2494  }
2495  if (($level < 0) OR ($level > 3)) {
2496  return 0;
2497  }
2498  return $this->formatInfo[$level][$mask];
2499  }
2500 
2508  protected function putFinderPattern($frame, $ox, $oy) {
2509  $finder = array(
2510  "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
2511  "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
2512  "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2513  "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2514  "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
2515  "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
2516  "\xc1\xc1\xc1\xc1\xc1\xc1\xc1"
2517  );
2518  for ($y=0; $y < 7; $y++) {
2519  $frame = $this->qrstrset($frame, $ox, ($oy + $y), $finder[$y]);
2520  }
2521  return $frame;
2522  }
2523 
2529  protected function createFrame($version) {
2530  $width = $this->capacity[$version][QRCAP_WIDTH];
2531  $frameLine = str_repeat ("\0", $width);
2532  $frame = array_fill(0, $width, $frameLine);
2533  // Finder pattern
2534  $frame = $this->putFinderPattern($frame, 0, 0);
2535  $frame = $this->putFinderPattern($frame, $width - 7, 0);
2536  $frame = $this->putFinderPattern($frame, 0, $width - 7);
2537  // Separator
2538  $yOffset = $width - 7;
2539  for ($y=0; $y < 7; ++$y) {
2540  $frame[$y][7] = "\xc0";
2541  $frame[$y][$width - 8] = "\xc0";
2542  $frame[$yOffset][7] = "\xc0";
2543  ++$yOffset;
2544  }
2545  $setPattern = str_repeat("\xc0", 8);
2546  $frame = $this->qrstrset($frame, 0, 7, $setPattern);
2547  $frame = $this->qrstrset($frame, $width-8, 7, $setPattern);
2548  $frame = $this->qrstrset($frame, 0, $width - 8, $setPattern);
2549  // Format info
2550  $setPattern = str_repeat("\x84", 9);
2551  $frame = $this->qrstrset($frame, 0, 8, $setPattern);
2552  $frame = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8);
2553  $yOffset = $width - 8;
2554  for ($y=0; $y < 8; ++$y,++$yOffset) {
2555  $frame[$y][8] = "\x84";
2556  $frame[$yOffset][8] = "\x84";
2557  }
2558  // Timing pattern
2559  $wo = $width - 15;
2560  for ($i=1; $i < $wo; ++$i) {
2561  $frame[6][7+$i] = chr(0x90 | ($i & 1));
2562  $frame[7+$i][6] = chr(0x90 | ($i & 1));
2563  }
2564  // Alignment pattern
2566  // Version information
2567  if ($version >= 7) {
2568  $vinf = $this->getVersionPattern($version);
2569  $v = $vinf;
2570  for ($x=0; $x<6; ++$x) {
2571  for ($y=0; $y<3; ++$y) {
2572  $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1));
2573  $v = $v >> 1;
2574  }
2575  }
2576  $v = $vinf;
2577  for ($y=0; $y<6; ++$y) {
2578  for ($x=0; $x<3; ++$x) {
2579  $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1));
2580  $v = $v >> 1;
2581  }
2582  }
2583  }
2584  // and a little bit...
2585  $frame[$width - 8][8] = "\x81";
2586  return $frame;
2587  }
2588 
2594  protected function newFrame($version) {
2595  if (($version < 1) OR ($version > QRSPEC_VERSION_MAX)) {
2596  return NULL;
2597  }
2598  if (!isset($this->frames[$version])) {
2599  $this->frames[$version] = $this->createFrame($version);
2600  }
2601  if (is_null($this->frames[$version])) {
2602  return NULL;
2603  }
2604  return $this->frames[$version];
2605  }
2606 
2612  protected function rsBlockNum($spec) {
2613  return ($spec[0] + $spec[3]);
2614  }
2615 
2621  protected function rsBlockNum1($spec) {
2622  return $spec[0];
2623  }
2624 
2630  protected function rsDataCodes1($spec) {
2631  return $spec[1];
2632  }
2633 
2639  protected function rsEccCodes1($spec) {
2640  return $spec[2];
2641  }
2642 
2648  protected function rsBlockNum2($spec) {
2649  return $spec[3];
2650  }
2651 
2657  protected function rsDataCodes2($spec) {
2658  return $spec[4];
2659  }
2660 
2666  protected function rsEccCodes2($spec) {
2667  return $spec[2];
2668  }
2669 
2675  protected function rsDataLength($spec) {
2676  return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]);
2677  }
2678 
2684  protected function rsEccLength($spec) {
2685  return ($spec[0] + $spec[3]) * $spec[2];
2686  }
2687 
2688  // - - - - - - - - - - - - - - - - - - - - - - - - -
2689 
2690  // QRrs
2691 
2702  protected function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
2703  foreach ($this->rsitems as $rs) {
2704  if (($rs['pad'] != $pad) OR ($rs['nroots'] != $nroots) OR ($rs['mm'] != $symsize)
2705  OR ($rs['gfpoly'] != $gfpoly) OR ($rs['fcr'] != $fcr) OR ($rs['prim'] != $prim)) {
2706  continue;
2707  }
2708  return $rs;
2709  }
2710  $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
2711  array_unshift($this->rsitems, $rs);
2712  return $rs;
2713  }
2714 
2715  // - - - - - - - - - - - - - - - - - - - - - - - - -
2716 
2717  // QRrsItem
2718 
2725  protected function modnn($rs, $x) {
2726  while ($x >= $rs['nn']) {
2727  $x -= $rs['nn'];
2728  $x = ($x >> $rs['mm']) + ($x & $rs['nn']);
2729  }
2730  return $x;
2731  }
2732 
2743  protected function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) {
2744  // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2)
2745  $rs = null;
2746  // Check parameter ranges
2747  if (($symsize < 0) OR ($symsize > 8)) {
2748  return $rs;
2749  }
2750  if (($fcr < 0) OR ($fcr >= (1<<$symsize))) {
2751  return $rs;
2752  }
2753  if (($prim <= 0) OR ($prim >= (1<<$symsize))) {
2754  return $rs;
2755  }
2756  if (($nroots < 0) OR ($nroots >= (1<<$symsize))) {
2757  return $rs;
2758  }
2759  if (($pad < 0) OR ($pad >= ((1<<$symsize) -1 - $nroots))) {
2760  return $rs;
2761  }
2762  $rs = array();
2763  $rs['mm'] = $symsize;
2764  $rs['nn'] = (1 << $symsize) - 1;
2765  $rs['pad'] = $pad;
2766  $rs['alpha_to'] = array_fill(0, ($rs['nn'] + 1), 0);
2767  $rs['index_of'] = array_fill(0, ($rs['nn'] + 1), 0);
2768  // PHP style macro replacement ;)
2769  $NN =& $rs['nn'];
2770  $A0 =& $NN;
2771  // Generate Galois field lookup tables
2772  $rs['index_of'][0] = $A0; // log(zero) = -inf
2773  $rs['alpha_to'][$A0] = 0; // alpha**-inf = 0
2774  $sr = 1;
2775  for ($i=0; $i<$rs['nn']; ++$i) {
2776  $rs['index_of'][$sr] = $i;
2777  $rs['alpha_to'][$i] = $sr;
2778  $sr <<= 1;
2779  if ($sr & (1 << $symsize)) {
2780  $sr ^= $gfpoly;
2781  }
2782  $sr &= $rs['nn'];
2783  }
2784  if ($sr != 1) {
2785  // field generator polynomial is not primitive!
2786  return NULL;
2787  }
2788  // Form RS code generator polynomial from its roots
2789  $rs['genpoly'] = array_fill(0, ($nroots + 1), 0);
2790  $rs['fcr'] = $fcr;
2791  $rs['prim'] = $prim;
2792  $rs['nroots'] = $nroots;
2793  $rs['gfpoly'] = $gfpoly;
2794  // Find prim-th root of 1, used in decoding
2795  for ($iprim=1; ($iprim % $prim) != 0; $iprim += $rs['nn']) {
2796  ; // intentional empty-body loop!
2797  }
2798  $rs['iprim'] = (int)($iprim / $prim);
2799  $rs['genpoly'][0] = 1;
2800  for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
2801  $rs['genpoly'][$i+1] = 1;
2802  // Multiply rs->genpoly[] by @**(root + x)
2803  for ($j = $i; $j > 0; --$j) {
2804  if ($rs['genpoly'][$j] != 0) {
2805  $rs['genpoly'][$j] = $rs['genpoly'][$j-1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)];
2806  } else {
2807  $rs['genpoly'][$j] = $rs['genpoly'][$j-1];
2808  }
2809  }
2810  // rs->genpoly[0] can never be zero
2811  $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)];
2812  }
2813  // convert rs->genpoly[] to index form for quicker encoding
2814  for ($i = 0; $i <= $nroots; ++$i) {
2815  $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]];
2816  }
2817  return $rs;
2818  }
2819 
2827  protected function encode_rs_char($rs, $data, $parity) {
2828  $MM =& $rs['mm']; // bits per symbol
2829  $NN =& $rs['nn']; // the total number of symbols in a RS block
2830  $ALPHA_TO =& $rs['alpha_to']; // the address of an array of NN elements to convert Galois field elements in index (log) form to polynomial form
2831  $INDEX_OF =& $rs['index_of']; // the address of an array of NN elements to convert Galois field elements in polynomial form to index (log) form
2832  $GENPOLY =& $rs['genpoly']; // an array of NROOTS+1 elements containing the generator polynomial in index form
2833  $NROOTS =& $rs['nroots']; // the number of roots in the RS code generator polynomial, which is the same as the number of parity symbols in a block
2834  $FCR =& $rs['fcr']; // first consecutive root, index form
2835  $PRIM =& $rs['prim']; // primitive element, index form
2836  $IPRIM =& $rs['iprim']; // prim-th root of 1, index form
2837  $PAD =& $rs['pad']; // the number of pad symbols in a block
2838  $A0 =& $NN;
2839  $parity = array_fill(0, $NROOTS, 0);
2840  for ($i=0; $i < ($NN - $NROOTS - $PAD); $i++) {
2841  $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
2842  if ($feedback != $A0) {
2843  // feedback term is non-zero
2844  // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
2845  // always be for the polynomials constructed by init_rs()
2846  $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback);
2847  for ($j=1; $j < $NROOTS; ++$j) {
2848  $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[($NROOTS - $j)])];
2849  }
2850  }
2851  // Shift
2852  array_shift($parity);
2853  if ($feedback != $A0) {
2854  array_push($parity, $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]);
2855  } else {
2856  array_push($parity, 0);
2857  }
2858  }
2859  return $parity;
2860  }
2861 
2862 } // end QRcode class
2863 
2864 //============================================================+
2865 // END OF FILE
2866 //============================================================+




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.